@winccoa-tools-pack/npm-winccoa-ctrl-code-style 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/cjs/cli.js +204 -0
- package/dist/cjs/cli.js.map +1 -0
- package/dist/cjs/index.js +29 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/paths.js +50 -0
- package/dist/cjs/paths.js.map +1 -0
- package/dist/cjs/register.js +154 -0
- package/dist/cjs/register.js.map +1 -0
- package/dist/cjs/style-check.js +110 -0
- package/dist/cjs/style-check.js.map +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/esm/cli.js +204 -0
- package/dist/esm/cli.js.map +1 -0
- package/dist/esm/index.js +29 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/paths.js +50 -0
- package/dist/esm/paths.js.map +1 -0
- package/dist/esm/register.js +154 -0
- package/dist/esm/register.js.map +1 -0
- package/dist/esm/style-check.js +110 -0
- package/dist/esm/style-check.js.map +1 -0
- package/dist/esm/types.js +3 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/paths.d.ts +16 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/register.d.ts +49 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/style-check.d.ts +31 -0
- package/dist/style-check.d.ts.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +68 -0
- package/scripts/approve-open-pr-workflows.sh +85 -0
- package/scripts/check-changelog-version.mjs +37 -0
- package/scripts/fix-esm-imports.mjs +115 -0
- package/scripts/generate-changelog.mjs +190 -0
- package/scripts/register-stylecheck-projects.ps1 +92 -0
- package/scripts/register-stylecheck-projects.sh +193 -0
- package/scripts/run-node-tests.ts +74 -0
- package/scripts/wait-for-winccoa.sh +26 -0
- package/winccoa/StyleCheck/scripts/astyle.ctl +142 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/* eslint-env node */
|
|
2
|
+
/* global console, process */
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { execFileSync } from 'node:child_process';
|
|
6
|
+
|
|
7
|
+
function runGit(args) {
|
|
8
|
+
return execFileSync('git', args, { encoding: 'utf8' }).trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function todayIsoDate() {
|
|
12
|
+
const d = new Date();
|
|
13
|
+
const yyyy = String(d.getFullYear()).padStart(4, '0');
|
|
14
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
15
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
16
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseArgs(argv) {
|
|
20
|
+
const args = new Set(argv);
|
|
21
|
+
const getValue = (flag) => {
|
|
22
|
+
const idx = argv.indexOf(flag);
|
|
23
|
+
if (idx === -1) return undefined;
|
|
24
|
+
return argv[idx + 1];
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
write: args.has('--write'),
|
|
29
|
+
fromTag: getValue('--from-tag'),
|
|
30
|
+
date: getValue('--date'),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getStableTags() {
|
|
35
|
+
// Only stable SemVer tags like v2.3.1 (no suffix)
|
|
36
|
+
const out = runGit(['tag', '--list', 'v[0-9]*.[0-9]*.[0-9]*', '--sort=version:refname']);
|
|
37
|
+
return out ? out.split(/\r?\n/).map((t) => t.trim()).filter(Boolean) : [];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function getCommitSubjects(range) {
|
|
41
|
+
const args = ['log', '--no-decorate', '--pretty=%s'];
|
|
42
|
+
if (range) args.push(range);
|
|
43
|
+
const out = runGit(args);
|
|
44
|
+
if (!out) return [];
|
|
45
|
+
return out
|
|
46
|
+
.split(/\r?\n/)
|
|
47
|
+
.map((s) => s.trim())
|
|
48
|
+
.filter(Boolean);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isNoiseSubject(subject) {
|
|
52
|
+
if (subject.startsWith('Merge ')) return true;
|
|
53
|
+
if (subject.startsWith('chore(release):')) return true;
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function categorize(subject) {
|
|
58
|
+
// Conventional commits: type(scope)!: subject
|
|
59
|
+
const match = /^(?<type>[a-z]+)(\([^\r\n()]+\))?(!)?:\s+(?<msg>.+)$/.exec(subject);
|
|
60
|
+
const type = match?.groups?.type;
|
|
61
|
+
const msg = match?.groups?.msg ?? subject;
|
|
62
|
+
|
|
63
|
+
switch (type) {
|
|
64
|
+
case 'feat':
|
|
65
|
+
return { section: 'Added', text: msg };
|
|
66
|
+
case 'fix':
|
|
67
|
+
return { section: 'Fixed', text: msg };
|
|
68
|
+
case 'perf':
|
|
69
|
+
case 'refactor':
|
|
70
|
+
return { section: 'Changed', text: msg };
|
|
71
|
+
case 'docs':
|
|
72
|
+
case 'build':
|
|
73
|
+
case 'ci':
|
|
74
|
+
case 'test':
|
|
75
|
+
case 'style':
|
|
76
|
+
case 'chore':
|
|
77
|
+
case 'revert':
|
|
78
|
+
case 'deps':
|
|
79
|
+
case 'deps-dev':
|
|
80
|
+
return { section: 'Changed', text: msg };
|
|
81
|
+
default:
|
|
82
|
+
return { section: 'Changed', text: subject };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function renderEntry({ version, date, itemsBySection }) {
|
|
87
|
+
const sectionsOrder = ['Added', 'Fixed', 'Changed'];
|
|
88
|
+
const lines = [];
|
|
89
|
+
|
|
90
|
+
lines.push(`## [${version}] - ${date}`);
|
|
91
|
+
lines.push('');
|
|
92
|
+
|
|
93
|
+
let any = false;
|
|
94
|
+
for (const section of sectionsOrder) {
|
|
95
|
+
const items = itemsBySection.get(section) ?? [];
|
|
96
|
+
if (items.length === 0) continue;
|
|
97
|
+
any = true;
|
|
98
|
+
lines.push(`### ${section}`);
|
|
99
|
+
lines.push('');
|
|
100
|
+
for (const item of items) {
|
|
101
|
+
lines.push(`- ${item}`);
|
|
102
|
+
}
|
|
103
|
+
lines.push('');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!any) {
|
|
107
|
+
lines.push('### Changed');
|
|
108
|
+
lines.push('');
|
|
109
|
+
lines.push('- Maintenance release');
|
|
110
|
+
lines.push('');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return lines.join('\n').trimEnd();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function insertIntoChangelog(changelogContent, entryMarkdown) {
|
|
117
|
+
const firstHeadingIdx = changelogContent.indexOf('\n## [');
|
|
118
|
+
if (firstHeadingIdx === -1) {
|
|
119
|
+
return `${changelogContent.trimEnd()}\n\n${entryMarkdown}\n`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const before = changelogContent.slice(0, firstHeadingIdx + 1); // keep leading newline
|
|
123
|
+
const after = changelogContent.slice(firstHeadingIdx + 1);
|
|
124
|
+
return `${before}${entryMarkdown}\n\n${after}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const { write, fromTag, date: dateArg } = parseArgs(process.argv.slice(2));
|
|
128
|
+
|
|
129
|
+
const repoRoot = path.resolve(process.cwd());
|
|
130
|
+
const packageJsonPath = path.join(repoRoot, 'package.json');
|
|
131
|
+
const changelogPath = path.join(repoRoot, 'CHANGELOG.md');
|
|
132
|
+
|
|
133
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
134
|
+
console.error(`::error::Missing package.json at ${packageJsonPath}`);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!fs.existsSync(changelogPath)) {
|
|
139
|
+
console.error(`::error::Missing CHANGELOG.md at ${changelogPath}`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
144
|
+
const version = String(pkg.version || '').trim();
|
|
145
|
+
if (!version) {
|
|
146
|
+
console.error('::error::package.json does not contain a valid "version" field');
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const changelog = fs.readFileSync(changelogPath, 'utf8');
|
|
151
|
+
const expectedHeadingPrefix = `## [${version}] - `;
|
|
152
|
+
const alreadyExists = changelog.includes(expectedHeadingPrefix);
|
|
153
|
+
|
|
154
|
+
let startTag = fromTag;
|
|
155
|
+
if (!startTag) {
|
|
156
|
+
const stableTags = getStableTags().filter((t) => t !== `v${version}`);
|
|
157
|
+
startTag = stableTags.length > 0 ? stableTags[stableTags.length - 1] : undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const range = startTag ? `${startTag}..HEAD` : undefined;
|
|
161
|
+
const subjects = getCommitSubjects(range)
|
|
162
|
+
.filter((s) => !isNoiseSubject(s));
|
|
163
|
+
|
|
164
|
+
const itemsBySection = new Map([
|
|
165
|
+
['Added', []],
|
|
166
|
+
['Fixed', []],
|
|
167
|
+
['Changed', []],
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
for (const subject of subjects) {
|
|
171
|
+
const { section, text } = categorize(subject);
|
|
172
|
+
itemsBySection.get(section)?.push(text);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const entry = renderEntry({ version, date: dateArg ?? todayIsoDate(), itemsBySection });
|
|
176
|
+
|
|
177
|
+
process.stdout.write(entry + '\n');
|
|
178
|
+
|
|
179
|
+
if (!write) {
|
|
180
|
+
process.exit(0);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (alreadyExists) {
|
|
184
|
+
console.error(`CHANGELOG already contains heading for v${version}; skipping write.`);
|
|
185
|
+
process.exit(0);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const updated = insertIntoChangelog(changelog, entry);
|
|
189
|
+
fs.writeFileSync(changelogPath, updated, 'utf8');
|
|
190
|
+
console.error(`✅ Inserted changelog entry for v${version} into CHANGELOG.md`);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Register StyleCheck (non-runnable) and a worker project (runnable) with StyleCheck as sub-project.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Uses @winccoa-tools-pack/npm-winccoa-register-project locally or in CI.
|
|
7
|
+
Prefer this on Windows; the .sh script is for Git Bash / Linux / CI containers.
|
|
8
|
+
|
|
9
|
+
.EXAMPLE
|
|
10
|
+
./scripts/register-stylecheck-projects.ps1 -ProjectPath ./src/Squirt -Version 3.21
|
|
11
|
+
#>
|
|
12
|
+
[CmdletBinding()]
|
|
13
|
+
param(
|
|
14
|
+
[Parameter(Mandatory = $true)]
|
|
15
|
+
[string] $ProjectPath,
|
|
16
|
+
|
|
17
|
+
[Parameter(Mandatory = $true)]
|
|
18
|
+
[Alias('v')]
|
|
19
|
+
[string] $Version,
|
|
20
|
+
|
|
21
|
+
[string] $Langs = 'en_US.utf8',
|
|
22
|
+
|
|
23
|
+
[string] $StyleCheckPath = '',
|
|
24
|
+
|
|
25
|
+
[string] $RegisterCli = '',
|
|
26
|
+
|
|
27
|
+
[switch] $DryRun
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
$ErrorActionPreference = 'Stop'
|
|
31
|
+
$PkgRoot = Split-Path -Parent $PSScriptRoot
|
|
32
|
+
if (-not $StyleCheckPath) {
|
|
33
|
+
$StyleCheckPath = Join-Path $PkgRoot 'winccoa\StyleCheck'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
$ProjectPath = (Resolve-Path -LiteralPath $ProjectPath).Path
|
|
37
|
+
$StyleCheckPath = (Resolve-Path -LiteralPath $StyleCheckPath).Path
|
|
38
|
+
|
|
39
|
+
function Find-RegisterCli {
|
|
40
|
+
if ($RegisterCli -and (Test-Path -LiteralPath $RegisterCli)) {
|
|
41
|
+
return (Resolve-Path -LiteralPath $RegisterCli).Path
|
|
42
|
+
}
|
|
43
|
+
$candidates = @(
|
|
44
|
+
(Join-Path $PkgRoot 'node_modules\@winccoa-tools-pack\npm-winccoa-register-project\dist\cjs\cli.js'),
|
|
45
|
+
(Join-Path $PkgRoot '..\npm-winccoa-register-project\dist\cjs\cli.js'),
|
|
46
|
+
(Join-Path $PkgRoot '..\npm-winccoa-register-project\dist\src\cli.js')
|
|
47
|
+
)
|
|
48
|
+
foreach ($c in $candidates) {
|
|
49
|
+
if (Test-Path -LiteralPath $c) { return (Resolve-Path -LiteralPath $c).Path }
|
|
50
|
+
}
|
|
51
|
+
$cmd = Get-Command npm-winccoa-register -ErrorAction SilentlyContinue
|
|
52
|
+
if ($cmd) { return $cmd.Source }
|
|
53
|
+
throw 'Could not find npm-winccoa-register-project CLI. Install the package or pass -RegisterCli.'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function Invoke-Register {
|
|
57
|
+
param([string[]] $Args)
|
|
58
|
+
$cli = Find-RegisterCli
|
|
59
|
+
Write-Host "register: $cli $($Args -join ' ')"
|
|
60
|
+
if ($DryRun) { return }
|
|
61
|
+
if ($cli -like '*.js') {
|
|
62
|
+
& node $cli @Args
|
|
63
|
+
} else {
|
|
64
|
+
& $cli @Args
|
|
65
|
+
}
|
|
66
|
+
if ($LASTEXITCODE -ne 0) {
|
|
67
|
+
throw "Registration failed with exit code $LASTEXITCODE"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Write-Host "StyleCheck (non-runnable): $StyleCheckPath"
|
|
72
|
+
Write-Host "Worker project (runnable): $ProjectPath"
|
|
73
|
+
Write-Host "WinCC OA version: $Version"
|
|
74
|
+
Write-Host "Langs: $Langs"
|
|
75
|
+
|
|
76
|
+
Invoke-Register -Args @(
|
|
77
|
+
'--project-path', $StyleCheckPath,
|
|
78
|
+
'--runnable', 'false',
|
|
79
|
+
'--wincc-oa-version', $Version
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
Invoke-Register -Args @(
|
|
83
|
+
'--project-path', $ProjectPath,
|
|
84
|
+
'--runnable', 'true',
|
|
85
|
+
'--langs', $Langs,
|
|
86
|
+
'--wincc-oa-version', $Version,
|
|
87
|
+
'--sub-project', $StyleCheckPath
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
Write-Host "Done."
|
|
91
|
+
Write-Host "Worker config: $(Join-Path $ProjectPath 'config\config')"
|
|
92
|
+
Write-Host "Next: winccoa-ctrl-style check `"$ProjectPath`" -v $Version --no-register"
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# register-stylecheck-projects.sh
|
|
3
|
+
#
|
|
4
|
+
# Register StyleCheck (non-runnable) and a worker/source project (runnable)
|
|
5
|
+
# with StyleCheck as --sub-project, using npm-winccoa-register-project.
|
|
6
|
+
#
|
|
7
|
+
# Usable locally and in CI.
|
|
8
|
+
#
|
|
9
|
+
# Example:
|
|
10
|
+
# ./scripts/register-stylecheck-projects.sh \
|
|
11
|
+
# --project-path ./src/Squirt \
|
|
12
|
+
# --version 3.21 \
|
|
13
|
+
# --langs en_US.utf8
|
|
14
|
+
#
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
PKG_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
19
|
+
|
|
20
|
+
PROJECT_PATH=""
|
|
21
|
+
VERSION=""
|
|
22
|
+
LANGS="en_US.utf8"
|
|
23
|
+
STYLE_CHECK_PATH="${PKG_ROOT}/winccoa/StyleCheck"
|
|
24
|
+
REGISTER_CLI=""
|
|
25
|
+
DRY_RUN=0
|
|
26
|
+
|
|
27
|
+
usage() {
|
|
28
|
+
cat <<EOF
|
|
29
|
+
Usage: $(basename "$0") --project-path <workerProject> [options]
|
|
30
|
+
|
|
31
|
+
Register:
|
|
32
|
+
1) StyleCheck as non-runnable WinCC OA project
|
|
33
|
+
2) Worker project as runnable with --sub-project StyleCheck
|
|
34
|
+
|
|
35
|
+
Options:
|
|
36
|
+
--project-path <path> Runnable worker/source project (required)
|
|
37
|
+
-v, --version <ver> WinCC OA version (e.g. 3.21)
|
|
38
|
+
--langs <csv> Languages (default: en_US.utf8)
|
|
39
|
+
--style-check-path <path> StyleCheck path (default: package winccoa/StyleCheck)
|
|
40
|
+
--register-cli <path> Path to npm-winccoa-register CLI js entry
|
|
41
|
+
--dry-run Print commands only
|
|
42
|
+
-h, --help Show help
|
|
43
|
+
|
|
44
|
+
Environment:
|
|
45
|
+
WINCCOA_VERSION Fallback for --version
|
|
46
|
+
WINCCOA_REGISTER_CLI Fallback for --register-cli
|
|
47
|
+
|
|
48
|
+
After success, run style check with worker config, e.g.:
|
|
49
|
+
WCCOActrl -config <worker>/config/config -n -log +stderr astyle.ctl <source>
|
|
50
|
+
# or:
|
|
51
|
+
winccoa-ctrl-style check <worker> -v <ver> --no-register
|
|
52
|
+
EOF
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
while [[ $# -gt 0 ]]; do
|
|
56
|
+
case "$1" in
|
|
57
|
+
--project-path)
|
|
58
|
+
PROJECT_PATH="${2:-}"; shift 2 ;;
|
|
59
|
+
-v|--version)
|
|
60
|
+
VERSION="${2:-}"; shift 2 ;;
|
|
61
|
+
--langs)
|
|
62
|
+
LANGS="${2:-}"; shift 2 ;;
|
|
63
|
+
--style-check-path)
|
|
64
|
+
STYLE_CHECK_PATH="${2:-}"; shift 2 ;;
|
|
65
|
+
--register-cli)
|
|
66
|
+
REGISTER_CLI="${2:-}"; shift 2 ;;
|
|
67
|
+
--dry-run)
|
|
68
|
+
DRY_RUN=1; shift ;;
|
|
69
|
+
-h|--help)
|
|
70
|
+
usage; exit 0 ;;
|
|
71
|
+
*)
|
|
72
|
+
echo "Unknown option: $1" >&2
|
|
73
|
+
usage >&2
|
|
74
|
+
exit 1 ;;
|
|
75
|
+
esac
|
|
76
|
+
done
|
|
77
|
+
|
|
78
|
+
VERSION="${VERSION:-${WINCCOA_VERSION:-}}"
|
|
79
|
+
REGISTER_CLI="${REGISTER_CLI:-${WINCCOA_REGISTER_CLI:-}}"
|
|
80
|
+
|
|
81
|
+
if [[ -z "${PROJECT_PATH}" ]]; then
|
|
82
|
+
echo "Error: --project-path is required" >&2
|
|
83
|
+
usage >&2
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
if [[ -z "${VERSION}" ]]; then
|
|
88
|
+
echo "Error: --version (or WINCCOA_VERSION) is required" >&2
|
|
89
|
+
exit 1
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Resolve absolute paths when possible
|
|
93
|
+
if command -v realpath >/dev/null 2>&1; then
|
|
94
|
+
PROJECT_PATH="$(realpath "${PROJECT_PATH}")"
|
|
95
|
+
STYLE_CHECK_PATH="$(realpath "${STYLE_CHECK_PATH}")"
|
|
96
|
+
else
|
|
97
|
+
PROJECT_PATH="$(cd "${PROJECT_PATH}" && pwd)"
|
|
98
|
+
STYLE_CHECK_PATH="$(cd "${STYLE_CHECK_PATH}" && pwd)"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
if [[ ! -d "${PROJECT_PATH}" ]]; then
|
|
102
|
+
echo "Error: project path does not exist: ${PROJECT_PATH}" >&2
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
if [[ ! -d "${STYLE_CHECK_PATH}" ]]; then
|
|
106
|
+
echo "Error: StyleCheck path does not exist: ${STYLE_CHECK_PATH}" >&2
|
|
107
|
+
exit 1
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
find_register_cli() {
|
|
111
|
+
if [[ -n "${REGISTER_CLI}" && -f "${REGISTER_CLI}" ]]; then
|
|
112
|
+
echo "${REGISTER_CLI}"
|
|
113
|
+
return 0
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
local candidates=(
|
|
117
|
+
"${PKG_ROOT}/node_modules/@winccoa-tools-pack/npm-winccoa-register-project/dist/cjs/cli.js"
|
|
118
|
+
"${PKG_ROOT}/../npm-winccoa-register-project/dist/cjs/cli.js"
|
|
119
|
+
"${PKG_ROOT}/../npm-winccoa-register-project/dist/src/cli.js"
|
|
120
|
+
)
|
|
121
|
+
local c
|
|
122
|
+
for c in "${candidates[@]}"; do
|
|
123
|
+
if [[ -f "${c}" ]]; then
|
|
124
|
+
echo "${c}"
|
|
125
|
+
return 0
|
|
126
|
+
fi
|
|
127
|
+
done
|
|
128
|
+
|
|
129
|
+
if command -v npm-winccoa-register >/dev/null 2>&1; then
|
|
130
|
+
echo "npm-winccoa-register"
|
|
131
|
+
return 0
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
return 1
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
run_cmd() {
|
|
138
|
+
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
|
139
|
+
printf '+'
|
|
140
|
+
printf ' %q' "$@"
|
|
141
|
+
printf '\n'
|
|
142
|
+
return 0
|
|
143
|
+
fi
|
|
144
|
+
"$@"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
REGISTER_BIN="$(find_register_cli)" || {
|
|
148
|
+
echo "Error: could not find npm-winccoa-register-project CLI." >&2
|
|
149
|
+
echo "Install @winccoa-tools-pack/npm-winccoa-register-project or pass --register-cli." >&2
|
|
150
|
+
exit 1
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
echo "StyleCheck (non-runnable): ${STYLE_CHECK_PATH}"
|
|
154
|
+
echo "Worker project (runnable): ${PROJECT_PATH}"
|
|
155
|
+
echo "WinCC OA version: ${VERSION}"
|
|
156
|
+
echo "Langs: ${LANGS}"
|
|
157
|
+
echo "Register CLI: ${REGISTER_BIN}"
|
|
158
|
+
|
|
159
|
+
# 1) StyleCheck non-runnable
|
|
160
|
+
if [[ "${REGISTER_BIN}" == *.js ]]; then
|
|
161
|
+
run_cmd node "${REGISTER_BIN}" \
|
|
162
|
+
--project-path "${STYLE_CHECK_PATH}" \
|
|
163
|
+
--runnable false \
|
|
164
|
+
--wincc-oa-version "${VERSION}"
|
|
165
|
+
else
|
|
166
|
+
run_cmd "${REGISTER_BIN}" \
|
|
167
|
+
--project-path "${STYLE_CHECK_PATH}" \
|
|
168
|
+
--runnable false \
|
|
169
|
+
--wincc-oa-version "${VERSION}"
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# 2) Worker runnable + StyleCheck sub-project
|
|
173
|
+
# Note: register-project only writes config when missing. For a forced rewrite
|
|
174
|
+
# use: winccoa-ctrl-style register <projectPath> -v <ver>
|
|
175
|
+
if [[ "${REGISTER_BIN}" == *.js ]]; then
|
|
176
|
+
run_cmd node "${REGISTER_BIN}" \
|
|
177
|
+
--project-path "${PROJECT_PATH}" \
|
|
178
|
+
--runnable true \
|
|
179
|
+
--langs "${LANGS}" \
|
|
180
|
+
--wincc-oa-version "${VERSION}" \
|
|
181
|
+
--sub-project "${STYLE_CHECK_PATH}"
|
|
182
|
+
else
|
|
183
|
+
run_cmd "${REGISTER_BIN}" \
|
|
184
|
+
--project-path "${PROJECT_PATH}" \
|
|
185
|
+
--runnable true \
|
|
186
|
+
--langs "${LANGS}" \
|
|
187
|
+
--wincc-oa-version "${VERSION}" \
|
|
188
|
+
--sub-project "${STYLE_CHECK_PATH}"
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
echo "Done."
|
|
192
|
+
echo "Worker config: ${PROJECT_PATH}/config/config"
|
|
193
|
+
echo "Next: winccoa-ctrl-style check \"${PROJECT_PATH}\" -v ${VERSION} --no-register"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
function collectTestFiles(rootDir: string): string[] {
|
|
6
|
+
const out: string[] = [];
|
|
7
|
+
const stack: string[] = [rootDir];
|
|
8
|
+
|
|
9
|
+
while (stack.length > 0) {
|
|
10
|
+
const dir = stack.pop()!;
|
|
11
|
+
let entries: fs.Dirent[];
|
|
12
|
+
try {
|
|
13
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
14
|
+
} catch {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
const fullPath = path.join(dir, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
stack.push(fullPath);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (entry.isFile() && entry.name.endsWith('.test.ts')) {
|
|
26
|
+
out.push(fullPath);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return out.sort();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Collect CLI args but ignore option-like args (starting with '-')
|
|
35
|
+
const rawArgs = process.argv.slice(2);
|
|
36
|
+
const targets = rawArgs.filter((a) => !a.startsWith('-'));
|
|
37
|
+
if (targets.length === 0) {
|
|
38
|
+
console.error('Usage: node --import tsx scripts/run-node-tests.ts <file|dir> [file|dir...]');
|
|
39
|
+
process.exit(2);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const cwd = process.cwd();
|
|
43
|
+
const files = targets.flatMap((t) => {
|
|
44
|
+
const resolved = path.resolve(cwd, t);
|
|
45
|
+
try {
|
|
46
|
+
const stat = fs.statSync(resolved);
|
|
47
|
+
if (stat.isFile() && resolved.endsWith('.test.ts')) {
|
|
48
|
+
return [resolved];
|
|
49
|
+
}
|
|
50
|
+
if (stat.isDirectory()) {
|
|
51
|
+
return collectTestFiles(resolved);
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// ignore missing targets
|
|
55
|
+
}
|
|
56
|
+
return [] as string[];
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (files.length === 0) {
|
|
60
|
+
console.error(`No '*.test.ts' files found under: ${targets.join(', ')}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Run tests one by one to better isolate failures
|
|
65
|
+
// Integration tests may leave resources hanging that interfere with subsequent tests
|
|
66
|
+
let failed = false;
|
|
67
|
+
for (const file of files) {
|
|
68
|
+
const args = ['--import', 'tsx', '--test', '--test-force-exit', file];
|
|
69
|
+
const result = spawnSync(process.execPath, args, { stdio: 'inherit' });
|
|
70
|
+
if (result.status !== 0) {
|
|
71
|
+
failed = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
process.exit(failed ? 1 : 0);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# Wait for WinCC OA in container to be ready.
|
|
3
|
+
# Usage: wait-for-winccoa.sh <container-name> <max-retries>
|
|
4
|
+
|
|
5
|
+
container="$1"
|
|
6
|
+
max_retries="${2:-60}"
|
|
7
|
+
delay=2
|
|
8
|
+
|
|
9
|
+
if [ -z "$container" ]; then
|
|
10
|
+
echo "Usage: $0 <container-name> [max-retries]"
|
|
11
|
+
exit 2
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
echo "Waiting for WinCC OA in container '$container' (max $max_retries attempts)..."
|
|
15
|
+
i=0
|
|
16
|
+
while [ $i -lt $max_retries ]; do
|
|
17
|
+
i=$((i+1))
|
|
18
|
+
docker exec "$container" sh -c "if [ -x /opt/WinCC_OA/bin/winccoa ]; then echo ready; exit 0; else exit 1; fi" >/dev/null 2>&1 && {
|
|
19
|
+
echo "Ready after $i attempts"
|
|
20
|
+
exit 0
|
|
21
|
+
}
|
|
22
|
+
sleep $delay
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
echo "Timed out waiting for WinCC OA in container '$container'"
|
|
26
|
+
exit 1
|