Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc8fa3a6fe | ||
|
|
24e57457b0 | ||
|
|
43fe5e0373 |
@@ -1,52 +0,0 @@
|
|||||||
# Publish the immutable crucible:<sha> image on version tags.
|
|
||||||
# PR/main test builds live in test-image.yml and never publish, so registry
|
|
||||||
# packages are only generated for releases we intend to use (not on every merge).
|
|
||||||
#
|
|
||||||
# Daemonless: the host-mode runner has no container runtime, so the image is
|
|
||||||
# built by Nix (`nix build .#image`, see flake.nix) and pushed with skopeo
|
|
||||||
# straight from the OCI tarball. No `docker build`/`docker login` involved.
|
|
||||||
name: build-image
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags: ['v*']
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: registry.westgate.pw
|
|
||||||
IMAGE: deployment/crucible
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: nix-docker
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with: { fetch-depth: 0 }
|
|
||||||
|
|
||||||
- name: Resolve tag
|
|
||||||
id: tag
|
|
||||||
run: echo "sha=$(git rev-parse --short=12 HEAD)" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Build OCI image (daemonless)
|
|
||||||
run: nix build .#image
|
|
||||||
|
|
||||||
# This workflow only runs on v* tags, so every run is a release publish.
|
|
||||||
- name: Publish image
|
|
||||||
env:
|
|
||||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
|
||||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
nix shell nixpkgs#skopeo -c skopeo copy \
|
|
||||||
--dest-creds "${REGISTRY_USER}:${REGISTRY_PASSWORD}" \
|
|
||||||
docker-archive:result \
|
|
||||||
"docker://${REGISTRY}/${IMAGE}:${{ steps.tag.outputs.sha }}"
|
|
||||||
|
|
||||||
- name: Emit release fragment
|
|
||||||
run: |
|
|
||||||
FRAG_REPO=sow-tools \
|
|
||||||
FRAG_SHA=${{ steps.tag.outputs.sha }} \
|
|
||||||
FRAG_ARTIFACT="${REGISTRY}/${IMAGE}:${{ steps.tag.outputs.sha }}" \
|
|
||||||
FRAG_URL="${REGISTRY}/${IMAGE}" \
|
|
||||||
FRAG_RUN_ID=${{ gitea.run_id }} \
|
|
||||||
bash scripts/emit-release-fragment.sh
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with: { name: release-fragment, path: release-fragment.json }
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# Manual compatibility publisher for the Crucible image.
|
|
||||||
# Normal release publishing happens in build-image.yml on v* tags; this is the
|
|
||||||
# break-glass / re-publish path, triggered by hand.
|
|
||||||
#
|
|
||||||
# Daemonless: built by Nix (`nix build .#image`) and pushed with skopeo from the
|
|
||||||
# OCI tarball. No `docker build`/`docker login` involved.
|
|
||||||
name: publish-image
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: registry.westgate.pw
|
|
||||||
IMAGE: deployment/crucible
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: nix-docker
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with: { fetch-depth: 0 }
|
|
||||||
|
|
||||||
- name: Resolve tag
|
|
||||||
id: tag
|
|
||||||
run: echo "sha=$(git rev-parse --short=12 HEAD)" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Build OCI image (daemonless)
|
|
||||||
run: nix build .#image
|
|
||||||
|
|
||||||
- name: Publish image
|
|
||||||
env:
|
|
||||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
|
||||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
nix shell nixpkgs#skopeo -c skopeo copy \
|
|
||||||
--dest-creds "${REGISTRY_USER}:${REGISTRY_PASSWORD}" \
|
|
||||||
docker-archive:result \
|
|
||||||
"docker://${REGISTRY}/${IMAGE}:${{ steps.tag.outputs.sha }}"
|
|
||||||
|
|
||||||
- name: Emit release fragment
|
|
||||||
run: |
|
|
||||||
FRAG_REPO=sow-tools \
|
|
||||||
FRAG_SHA=${{ steps.tag.outputs.sha }} \
|
|
||||||
FRAG_ARTIFACT="${REGISTRY}/${IMAGE}:${{ steps.tag.outputs.sha }}" \
|
|
||||||
FRAG_URL="${REGISTRY}/${IMAGE}" \
|
|
||||||
FRAG_RUN_ID=${{ gitea.run_id }} \
|
|
||||||
bash scripts/emit-release-fragment.sh
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with: { name: release-fragment, path: release-fragment.json }
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# Test-build the Crucible image on PRs. Proves `nix build .#image` still works
|
|
||||||
# (daemonless, Nix-built OCI tarball) but does NOT publish — release publishing
|
|
||||||
# happens in build-image.yml on v* tags. PR-only: with up-to-date-before-merge
|
|
||||||
# protection, main == the tested PR head, so a throwaway post-merge rebuild that
|
|
||||||
# publishes nothing is pure waste.
|
|
||||||
name: test-image
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-image:
|
|
||||||
runs-on: nix-docker
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with: { fetch-depth: 0 }
|
|
||||||
|
|
||||||
- name: Build OCI image (daemonless, no publish)
|
|
||||||
run: nix build .#image
|
|
||||||
@@ -41,13 +41,14 @@ section and `.gitea/workflows/`.
|
|||||||
- Dev loop: `nix develop`, then `make check` / `make build` / `make smoke`
|
- Dev loop: `nix develop`, then `make check` / `make build` / `make smoke`
|
||||||
(see Commands below).
|
(see Commands below).
|
||||||
- Release: push a `v*` tag. CI uploads cross-built binaries + wrappers to the
|
- Release: push a `v*` tag. CI uploads cross-built binaries + wrappers to the
|
||||||
Gitea release and publishes the `crucible` container image.
|
Gitea release. There is no container image; Crucible ships as binaries,
|
||||||
|
wrappers, and the Nix input.
|
||||||
- Consumers (they download released binaries via the wrapper; they never
|
- Consumers (they download released binaries via the wrapper; they never
|
||||||
vendor a toolkit):
|
vendor a toolkit):
|
||||||
- sow-module — https://git.westgate.pw/ShadowsOverWestgate/sow-module
|
- sow-module — https://git.westgate.pw/ShadowsOverWestgate/sow-module
|
||||||
- sow-topdata — https://git.westgate.pw/ShadowsOverWestgate/sow-topdata
|
- sow-topdata — https://git.westgate.pw/ShadowsOverWestgate/sow-topdata
|
||||||
- sow-assets-manifest — https://git.westgate.pw/ShadowsOverWestgate/sow-assets-manifest
|
- sow-assets-manifest — https://git.westgate.pw/ShadowsOverWestgate/sow-assets-manifest
|
||||||
- sow-platform (deploys the released image/pins) —
|
- sow-platform (infra/deploy authority) —
|
||||||
https://git.westgate.pw/ShadowsOverWestgate/sow-platform
|
https://git.westgate.pw/ShadowsOverWestgate/sow-platform
|
||||||
|
|
||||||
## What this repo owns / does not own
|
## What this repo owns / does not own
|
||||||
@@ -62,7 +63,7 @@ is `sow-platform`).
|
|||||||
|
|
||||||
1. **Fail closed, never fake.** A builder with no migrated logic yet (`depot`)
|
1. **Fail closed, never fake.** A builder with no migrated logic yet (`depot`)
|
||||||
exits `70`. Do not stub a builder to emit a placeholder artifact.
|
exits `70`. Do not stub a builder to emit a placeholder artifact.
|
||||||
2. **Binaries are not committed.** They are CI artifacts / image layers.
|
2. **Binaries are not committed.** They are CI artifacts.
|
||||||
`/bin/`, `*.exe`, `nwn-tool`, `sow-toolkit` are gitignored.
|
`/bin/`, `*.exe`, `nwn-tool`, `sow-toolkit` are gitignored.
|
||||||
3. **The registry is the command surface.** `internal/dispatch.Registry` is the
|
3. **The registry is the command surface.** `internal/dispatch.Registry` is the
|
||||||
single source of truth; keep it in sync with `cmd/` and
|
single source of truth; keep it in sync with `cmd/` and
|
||||||
@@ -81,7 +82,6 @@ is `sow-platform`).
|
|||||||
nix develop && make check # vet + test + shellcheck + yamllint
|
nix develop && make check # vet + test + shellcheck + yamllint
|
||||||
make build # cmd/* -> ./bin
|
make build # cmd/* -> ./bin
|
||||||
make smoke # assert fail-closed contract
|
make smoke # assert fail-closed contract
|
||||||
make image # crucible:<sha>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
SHELL := bash
|
SHELL := bash
|
||||||
.ONESHELL:
|
.ONESHELL:
|
||||||
.PHONY: check test vet build smoke fmt image
|
.PHONY: check test vet build smoke fmt
|
||||||
|
|
||||||
# Lint + unit tests. Mirrors the `test` CI job; runs green inside `nix develop`.
|
# Lint + unit tests. Mirrors the `test` CI job; runs green inside `nix develop`.
|
||||||
check: vet test
|
check: vet test
|
||||||
@@ -22,9 +22,3 @@ smoke: build
|
|||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
gofmt -l -w cmd internal
|
gofmt -l -w cmd internal
|
||||||
|
|
||||||
# Build the Crucible image locally. Requires docker; mirrors build-image CI.
|
|
||||||
IMAGE ?= crucible
|
|
||||||
GIT_SHA ?= $(shell git rev-parse --short=12 HEAD 2>/dev/null || echo unknown)
|
|
||||||
image:
|
|
||||||
docker build --build-arg GIT_SHA=$(GIT_SHA) -f docker/Dockerfile -t $(IMAGE):$(GIT_SHA) .
|
|
||||||
|
|||||||
@@ -71,10 +71,9 @@ nix develop # Go + shellcheck + yamllint + make
|
|||||||
make check # go vet + go test + shellcheck + yamllint
|
make check # go vet + go test + shellcheck + yamllint
|
||||||
make build # build every cmd/* into ./bin (gitignored)
|
make build # build every cmd/* into ./bin (gitignored)
|
||||||
make smoke # build + assert the fail-closed contract
|
make smoke # build + assert the fail-closed contract
|
||||||
make image # docker build -> crucible:<sha>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Binaries are **never committed** — they are CI artifacts / image layers (D19).
|
Binaries are **never committed** — they are CI artifacts (D19).
|
||||||
This retires the old habit of checking in `nwn-tool` / `sow-toolkit`.
|
This retires the old habit of checking in `nwn-tool` / `sow-toolkit`.
|
||||||
|
|
||||||
## CI
|
## CI
|
||||||
@@ -84,12 +83,8 @@ publish event is a `v*` tag (see `runbooks/ci-trigger-standard.md` in sow-docs,
|
|||||||
https://git.westgate.pw/ShadowsOverWestgate/sow-docs).
|
https://git.westgate.pw/ShadowsOverWestgate/sow-docs).
|
||||||
|
|
||||||
- `test.yml` — vet, test, shellcheck, yamllint, binary smoke (PR + main).
|
- `test.yml` — vet, test, shellcheck, yamllint, binary smoke (PR + main).
|
||||||
- `test-image.yml` — build the OCI image to prove it compiles (PR + main, no push).
|
|
||||||
- `build-binaries.yml` — cross-build all targets (PR + main); on a `v*` tag, upload
|
- `build-binaries.yml` — cross-build all targets (PR + main); on a `v*` tag, upload
|
||||||
the binaries, `SHA256SUMS`, and the wrappers to the Gitea release.
|
the binaries, `SHA256SUMS`, and the wrappers to the Gitea release.
|
||||||
- `build-image.yml` — on a `v*` tag, build and publish
|
|
||||||
`registry.westgate.pw/deployment/crucible:<sha>`.
|
|
||||||
- `publish-image.yml` — manual `workflow_dispatch` break-glass republish.
|
|
||||||
- `sync-wrappers.yml` — on a `main` push that touches `wrappers/`, auto-PR the
|
- `sync-wrappers.yml` — on a `main` push that touches `wrappers/`, auto-PR the
|
||||||
canonical wrappers to the consumer repos in `wrappers/consumers.txt`.
|
canonical wrappers to the consumer repos in `wrappers/consumers.txt`.
|
||||||
Consumer drift checks run after those PRs merge to `main`, not on the PRs
|
Consumer drift checks run after those PRs merge to `main`, not on the PRs
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
# syntax=docker/dockerfile:1
|
|
||||||
#
|
|
||||||
# Crucible toolchain image: registry.westgate.pw/deployment/crucible:<git-sha>
|
|
||||||
#
|
|
||||||
# Reproducible multi-stage build, no on-VPS build. Produces every cmd/* binary
|
|
||||||
# and ships them on a static base. The `crucible` dispatcher is the entrypoint;
|
|
||||||
# consumer CI can also call the standalone crucible-<name> binaries by path.
|
|
||||||
#
|
|
||||||
FROM golang:1.26-alpine AS build
|
|
||||||
WORKDIR /src
|
|
||||||
RUN apk add --no-cache git
|
|
||||||
# go.sum is committed now that the migrated packages pull golang.org/x/text and
|
|
||||||
# gopkg.in/yaml.v3; the glob keeps the build working if it is ever absent.
|
|
||||||
COPY go.mod go.sum* ./
|
|
||||||
RUN go mod download
|
|
||||||
COPY . .
|
|
||||||
ARG GIT_SHA=unknown
|
|
||||||
ENV CGO_ENABLED=0
|
|
||||||
RUN set -eux; \
|
|
||||||
mkdir -p /out; \
|
|
||||||
for dir in ./cmd/*/; do \
|
|
||||||
name="$(basename "${dir}")"; \
|
|
||||||
go build -trimpath \
|
|
||||||
-ldflags "-s -w -X git.westgate.pw/ShadowsOverWestgate/sow-tools/internal/buildinfo.Version=${GIT_SHA}" \
|
|
||||||
-o "/out/${name}" "${dir}"; \
|
|
||||||
done
|
|
||||||
|
|
||||||
FROM debian:12-slim
|
|
||||||
# ca-certificates: builders fetch published manifests over HTTPS.
|
|
||||||
RUN set -eux; \
|
|
||||||
apt-get update; \
|
|
||||||
apt-get install -y --no-install-recommends ca-certificates; \
|
|
||||||
rm -rf /var/lib/apt/lists/*; \
|
|
||||||
useradd --system --create-home --uid 65532 nonroot
|
|
||||||
COPY --from=build /out/ /usr/local/bin/
|
|
||||||
USER nonroot
|
|
||||||
ENTRYPOINT ["/usr/local/bin/crucible"]
|
|
||||||
CMD ["help"]
|
|
||||||
@@ -49,10 +49,10 @@ by `flake.lock`. CI runs the _same_ `crucible <build>` as local dev, inside the
|
|||||||
nix devshell (binary-cache fast). No build container, no token, no CI-only
|
nix devshell (binary-cache fast). No build container, no token, no CI-only
|
||||||
pre-steps.
|
pre-steps.
|
||||||
|
|
||||||
The `registry.westgate.pw/deployment/crucible:<sha>` image is **deployment-only**:
|
The `registry.westgate.pw/deployment/crucible:<sha>` image is **retired**
|
||||||
`prod.yml` pins it so tools travel with the runtime host (`nwn.enable`), a
|
(2026-07-17): nothing consumed it, `prod.yml` no longer reserves a slot for
|
||||||
disabled placeholder until the NWN stack lands. It is **not** a build tool and no
|
it, and CI no longer builds or publishes it. Binaries, wrappers, and the Nix
|
||||||
build/CI job consumes it.
|
input are the only supported ways to run Crucible.
|
||||||
|
|
||||||
## Determinism
|
## Determinism
|
||||||
|
|
||||||
|
|||||||
@@ -2317,6 +2317,18 @@ func (c *featGeneratedContext) familyHasExistingRows(familyKey string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skillsDatasetName returns the dataset the skill_focus family sources its
|
||||||
|
// children from. The skills dataset location is data-driven via that spec;
|
||||||
|
// "skills" is only the fallback when no spec is loaded.
|
||||||
|
func (c *featGeneratedContext) skillsDatasetName() string {
|
||||||
|
for key, spec := range c.familySpecs {
|
||||||
|
if normalizeKeyIdentity(key) == "skillfocus" && spec.ChildSource.Dataset != "" {
|
||||||
|
return spec.ChildSource.Dataset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "skills"
|
||||||
|
}
|
||||||
|
|
||||||
func (c *featGeneratedContext) datasetRows(name string) (map[string]map[string]any, error) {
|
func (c *featGeneratedContext) datasetRows(name string) (map[string]map[string]any, error) {
|
||||||
if rows, ok := c.rowsByDataset[name]; ok {
|
if rows, ok := c.rowsByDataset[name]; ok {
|
||||||
return rows, nil
|
return rows, nil
|
||||||
@@ -2555,7 +2567,10 @@ func buildFamilyExpansionGeneratedModule(path string, obj map[string]any, ctx *f
|
|||||||
if !include {
|
if !include {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
slug := strings.TrimPrefix(sourceKey, spec.ChildSource.Dataset+":")
|
slug := sourceKey
|
||||||
|
if idx := strings.Index(slug, ":"); idx >= 0 {
|
||||||
|
slug = slug[idx+1:]
|
||||||
|
}
|
||||||
featKey, rowID, hasID, err := ctx.resolveGeneratedFeatIdentity(spec, slug, row)
|
featKey, rowID, hasID, err := ctx.resolveGeneratedFeatIdentity(spec, slug, row)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("generated feat file %s: %w", path, err)
|
return nil, fmt.Errorf("generated feat file %s: %w", path, err)
|
||||||
@@ -2655,7 +2670,7 @@ func buildRacialtypesSkillAffinityModule(ctx *featGeneratedContext) (map[string]
|
|||||||
if len(grants) == 0 {
|
if len(grants) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
skillRows, err := ctx.datasetRows("skills")
|
skillRows, err := ctx.datasetRows(ctx.skillsDatasetName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -2854,7 +2869,7 @@ func (c *featGeneratedContext) displayNameForGeneratedSource(dataset string, row
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
switch dataset {
|
switch dataset {
|
||||||
case "skills":
|
case c.skillsDatasetName():
|
||||||
return displayNameForSkill(row)
|
return displayNameForSkill(row)
|
||||||
case "baseitems":
|
case "baseitems":
|
||||||
return displayNameForBaseitem(row)
|
return displayNameForBaseitem(row)
|
||||||
@@ -3457,10 +3472,20 @@ func legacyFamilyChildAliases(familyKey, sourceSlug string) []string {
|
|||||||
aliases = append(aliases, "craftweapon")
|
aliases = append(aliases, "craftweapon")
|
||||||
case "craftwoodworking":
|
case "craftwoodworking":
|
||||||
aliases = append(aliases, "crafttrap")
|
aliases = append(aliases, "crafttrap")
|
||||||
|
case "diplomacy":
|
||||||
|
aliases = append(aliases, "influence", "persuade")
|
||||||
case "disabledevice":
|
case "disabledevice":
|
||||||
aliases = append(aliases, "disabletrap")
|
aliases = append(aliases, "disabletrap")
|
||||||
case "influence":
|
case "influence":
|
||||||
aliases = append(aliases, "persuade")
|
aliases = append(aliases, "persuade")
|
||||||
|
case "intimidate":
|
||||||
|
aliases = append(aliases, "taunt")
|
||||||
|
case "investigation":
|
||||||
|
aliases = append(aliases, "search")
|
||||||
|
case "medicine":
|
||||||
|
aliases = append(aliases, "heal")
|
||||||
|
case "security":
|
||||||
|
aliases = append(aliases, "openlock")
|
||||||
case "sleightofhand":
|
case "sleightofhand":
|
||||||
aliases = append(aliases, "pickpocket")
|
aliases = append(aliases, "pickpocket")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1867,8 +1867,10 @@ func validateGeneratedFeatFamilies(dataDir string, report *ValidationReport) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
required := []requiredFeatFamily{
|
required := []requiredFeatFamily{
|
||||||
{FamilyKey: "skillfocus", Dataset: "skills", Predicate: "accessible"},
|
// skill families: the source dataset is data-driven (family spec), only
|
||||||
{FamilyKey: "greaterskillfocus", Dataset: "skills", Predicate: "accessible"},
|
// the accessibility predicate is required
|
||||||
|
{FamilyKey: "skillfocus", Predicate: "accessible"},
|
||||||
|
{FamilyKey: "greaterskillfocus", Predicate: "accessible"},
|
||||||
{FamilyKey: "weaponfocus", Dataset: "baseitems", Column: "WeaponFocusFeat"},
|
{FamilyKey: "weaponfocus", Dataset: "baseitems", Column: "WeaponFocusFeat"},
|
||||||
{FamilyKey: "weaponspecialization", Dataset: "baseitems", Column: "WeaponSpecializationFeat"},
|
{FamilyKey: "weaponspecialization", Dataset: "baseitems", Column: "WeaponSpecializationFeat"},
|
||||||
{FamilyKey: "improvedcritical", Dataset: "baseitems", Column: "WeaponImprovedCriticalFeat"},
|
{FamilyKey: "improvedcritical", Dataset: "baseitems", Column: "WeaponImprovedCriticalFeat"},
|
||||||
@@ -1882,7 +1884,7 @@ func validateGeneratedFeatFamilies(dataDir string, report *ValidationReport) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if spec.ChildSource.Dataset != requirement.Dataset ||
|
if (requirement.Dataset != "" && spec.ChildSource.Dataset != requirement.Dataset) ||
|
||||||
spec.ChildSource.Column != requirement.Column ||
|
spec.ChildSource.Column != requirement.Column ||
|
||||||
spec.ChildSource.Predicate != requirement.Predicate {
|
spec.ChildSource.Predicate != requirement.Predicate {
|
||||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||||
@@ -2081,7 +2083,10 @@ func validateGeneratedFeatFamilyCompleteness(path string, spec familyExpansionSp
|
|||||||
}
|
}
|
||||||
if include {
|
if include {
|
||||||
if familyAllowlist {
|
if familyAllowlist {
|
||||||
slug := strings.TrimPrefix(sourceKey, spec.ChildSource.Dataset+":")
|
slug := sourceKey
|
||||||
|
if idx := strings.Index(slug, ":"); idx >= 0 {
|
||||||
|
slug = slug[idx+1:]
|
||||||
|
}
|
||||||
featKey, _, _, err := ctx.resolveGeneratedFeatIdentity(spec, slug, row)
|
featKey, _, _, err := ctx.resolveGeneratedFeatIdentity(spec, slug, row)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||||
|
|||||||
@@ -569,13 +569,15 @@ func loadWikiContext(dataDir, sourceDir string) (*wikiContext, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
loadBase := func(name string) (nativeCollectedDataset, bool, error) {
|
loadBase := func(names ...string) (nativeCollectedDataset, bool, error) {
|
||||||
|
for _, name := range names {
|
||||||
for _, dataset := range datasets {
|
for _, dataset := range datasets {
|
||||||
if dataset.Name == name {
|
if dataset.Name == name {
|
||||||
collected, err := collectNativeDataset(dataset)
|
collected, err := collectNativeDataset(dataset)
|
||||||
return collected, true, err
|
return collected, true, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nativeCollectedDataset{}, false, nil
|
return nativeCollectedDataset{}, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,7 +585,7 @@ func loadWikiContext(dataDir, sourceDir string) (*wikiContext, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
skillDataset, skillOK, err := loadBase("skills")
|
skillDataset, skillOK, err := loadBase("skills", "skills/core")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user