release.yaml starts the SASjs Server the same way as build.yaml and
lighthouse.yaml, so the same root guard (sasjs/server v1.9.0) breaks it.
It runs on pushes to main and has not run since the guard shipped, so
the next release would have failed at the mock deploy.
Recent sasjs/server releases refuse to run as root unless RUN_AS names
an account (security hardening, "refuse to run as root unless RUN_AS
names an account"). The CI runners are root, so the server exited at
startup and pm2 restart-looped (103 restarts), leaving :5000
unreachable - which is what failed the mocked-estate deploy and the
Lighthouse job.
The server's own message offers RUN_AS=root as the deliberate override;
opt in explicitly for the throwaway CI container.
Both workflows start api-linux with `pm2 start api-linux --wait-ready`
and then hit http://localhost:5000 in the next step. pm2 can return
while the app is still 'launching' (it does not always block on the
ready signal), so the deploy step races the server and fails with
"Couldn't connect to server".
Wait up to 120s for the server to answer, and print `pm2 list` plus the
api-linux log if it never does, so a genuine startup failure is visible
in the job output instead of a bare ECONNREFUSED.
Adds a fourth admin action to the System information screen - Export DC
Library DDL - with a secondary flavour chooser (SAS, PGSQL, TSQL) and an
optional schema box for the DB flavours. The selection is passed to the
existing services/admin/exportdb service in a new window as URL params.
Also bumps @sasjs/adapter to ^4.19.1, adds a Cypress spec for the three
flavour states, and adds a TSQL case to the exportdb SAS test.
Closes#179
hook-programs.cy.ts was in no CI spec list, so it never ran - the only
end-to-end coverage of the new mock hook execution (mockHookSource plus
the PRE/POST_EDIT_HOOK blocks) and of the loader's target re-registration
check was dead weight. Add it to the development job's --spec list.
It goes second, not last: csv-limited.cy.ts asserts the free tier and so
must keep the first slot (before any spec applies a licence key), and test
2 reads the approval queue, which is paged oldest first - a changeset
submitted by an earlier spec pushes its own row off the first page. The
spec header records the constraint. Full list verified locally: 15 specs,
132 tests, all passing.
browser-info.cy.ts asserts the diagnostics tables on the adapter payload but was not in the job's explicit --spec list, so it never ran. Append it so the feature is covered by CI.
The server-ci target streams the Angular frontend (CONTRIBUTING.md documents
http://localhost:5000/AppStream/clickme/), but webSourcePath was
"`../../../../client/dist". The leading backtick makes the first path segment
literal, so it happened to resolve to <repo>/client/dist by accident; the
correct value relative to the sasjs project root (sas/mocks) is
../../client/dist.
The real problem was that CI job 2 never built the client, so client/dist did
not exist when the mock services were deployed: the deploy logged
"webSourcePath: <repo>/client/dist present in 'streamConfig' doesn't exist"
and streamed no frontend, while the job still passed. Build the production
frontend before deploying the mocks so the streamed app is actually produced.
The release job now hashes every asset it uploads (frontend.zip, the SAS 9
and Viya deployment programs, the SASjs Server bundle) into a SHA256SUMS
file with basename-only paths, and attaches it to the release alongside
the assets. The release body gains a "Verifying the download" section -
emitted by .gitea/scripts/verify-section.sh so the YAML scalar stays
clean - with the sha256sum -c command and a note on what a checksum does
and does not prove.
The upload loop is rewritten from an inline list to a bash array shared
by the hashing and upload steps, so the asset set cannot drift between
what is hashed and what is uploaded.
Simulated end to end locally (stubbed curl/jq): 8 uploads fire with
correct paths, SHA256SUMS verifies with sha256sum -c, a tampered file
fails the check, and the assembled release body keeps the existing
notes with the new section and installation footer on their own lines.
The release job zipped ./client/dist, so every archive entry carried a
client/dist/ prefix and an unzip dropped the app into a client/dist
subfolder. Zip from inside dist instead, so index.html, assets and the
bundled viya.json sit at the root of the archive.
Closes#147
The viewer's search box searches every column of a table - character columns
by case-sensitive CONTAINS and numeric columns by exact equality - but against
the JS mock backend the box did nothing, because the viewdata mock ignored
SEARCHTYPE/SEARCHVAL (and FILTER_RK) entirely.
Mock changes (sas/mocks/sasjs/services/public/viewdata.js):
- apply the stored filter first via mpeFilterMaster, mirroring
%mpe_filtermaster(VIEW,...) in viewdata.sas, and return its WHERE text as
sasparams.FILTER_TEXT (previously hard-coded blank, so the viewer's info bar
never showed a clause and the filter was never applied to the rows)
- mirror %mp_searchdata: CHAR = case-sensitive CONTAINS across every character
column, NUM = exact equality across every numeric column, only CHAR/NUM
trigger a search, search values stripped of % & ; " like the SAS service
- cap the rows the way the real service does (MAXROWS 500; NOBS is the uncapped
filtered count for a normal view, the capped match count for a search)
- a search with no matches now returns no rows, so the client shows its
"No data found with given conditions" panel; the single-empty-row fallback
stays for the normal-view-empty-table case
Mock data (sas/mocks/sasjs/services/admin/makedata.js):
- new MPE_X_SEARCH demo table: 1000 deterministic, obviously fictional
deep-sea survey rows, registered in MPE_TABLES. Seeded so a partial search
hits a value buried in the middle of a long text column, a value spanning two
columns, a repeated exact numeric, and rows that deliberately do not match.
New spec (client/cypress/e2e/full-table-search.cy.ts):
- seven tests covering open/full view, partial character search, case
sensitivity, numeric exact match, no match, search within a filter, and
clearing the search. No artificial waits - state assertions only.
- added to the Cypress spec list in .gitea/workflows/build.yaml
Also adds the dc-cypress skill (.agents/skills/dc-cypress) covering the mock
backend setup, the spec conventions, the selectors that actually work in the
viewer filter modal, and the fixed 1280x720 video capture.
- Mirrors the review page's toggle: getstagetable.sas now also webouts
fmt_stagetable (SAS-formatted), selectFormattedRows() picks between it
and the raw stagetable, falling back to raw if fmt data is absent
- Adds stage.cy.ts e2e coverage and wires it into both build.yaml and
release.yaml's cypress --spec lists (previously out of sync with
each other on viewbox.cy.ts too)
Adds a URL-driven toggle (?labels=true, mirroring embed=va) that
swaps grid column headers between NAME and LABEL in both the Viewer
and Editor, with a "Show labels"/"Show names" item in each grid's
right-click context menu. The header-info popup now shows NAME first
regardless of display mode. Sorting/filtering/data binding stay
NAME-based throughout — only the displayed header text changes.
New shared, unit-tested utils (parse-labels-param, display-col-headers,
col-info-html) back both pages. Mock data (viewdata.js, getdata.js)
gains a couple of columns with LABEL != NAME plus a blank-LABEL case,
covered by a new viewer-labels.cy.ts e2e spec now wired into CI.
Also fixes a pre-existing gap in cypress/support/commands.ts: the
isLoggedIn/loginAndUpdateValidKey custom commands had no Cypress
Chainable type augmentation, and moment was imported as a namespace
import despite its `export =` typing making it uncallable that way.