@smartsoft001-mobilems/claude-plugins 2.88.0 → 2.90.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/README.md +27 -2
- package/package.json +1 -1
- package/plugins/mobilems/.claude-plugin/README.md +21 -2
- package/plugins/mobilems/.claude-plugin/plugin.json +1 -1
- package/plugins/mobilems/skills/mobilems-articles/SKILL.md +52 -20
- package/plugins/mobilems/skills/mobilems-articles/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-articles/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-articles/scripts/verify-module.sh +53 -0
- package/plugins/mobilems/skills/mobilems-devops-config/SKILL.md +141 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/docker.md +58 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/env-and-lint.md +59 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/github-hygiene.md +54 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/tooling.md +105 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/workflows.md +410 -0
- package/plugins/mobilems/skills/mobilems-devops-config/scripts/detect-foundation.sh +47 -0
- package/plugins/mobilems/skills/mobilems-devops-config/scripts/verify-devops.sh +96 -0
- package/plugins/mobilems/skills/mobilems-events/SKILL.md +27 -1
- package/plugins/mobilems/skills/mobilems-events/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-events/scripts/verify-module.sh +43 -0
- package/plugins/mobilems/skills/mobilems-object-collections/SKILL.md +46 -14
- package/plugins/mobilems/skills/mobilems-object-collections/references/lib-contents.md +11 -0
- package/plugins/mobilems/skills/mobilems-object-collections/references/module-and-routes.md +26 -0
- package/plugins/mobilems/skills/mobilems-object-collections/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-object-collections/scripts/verify-module.sh +54 -0
- package/plugins/mobilems/skills/mobilems-objects/SKILL.md +42 -10
- package/plugins/mobilems/skills/mobilems-objects/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-objects/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-objects/scripts/verify-module.sh +53 -0
- package/plugins/mobilems/skills/mobilems-partners/SKILL.md +30 -2
- package/plugins/mobilems/skills/mobilems-partners/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-partners/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-partners/scripts/verify-module.sh +53 -0
- package/plugins/mobilems/skills/mobilems-project-init/SKILL.md +68 -8
- package/plugins/mobilems/skills/mobilems-project-init/references/branding-reset.md +112 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/claude-config.md +7 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/config-providers.md +1 -1
- package/plugins/mobilems/skills/mobilems-project-init/references/qa-hardening.md +30 -30
- package/plugins/mobilems/skills/mobilems-project-init/references/shared-lib.md +42 -5
- package/plugins/mobilems/skills/mobilems-project-init/references/tailwind-styles.md +33 -23
- package/plugins/mobilems/skills/mobilems-project-init/scripts/verify-init.sh +62 -9
- package/plugins/mobilems/skills/mobilems-thematic-publications/SKILL.md +25 -1
- package/plugins/mobilems/skills/mobilems-thematic-publications/references/lib-contents.md +11 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/references/module-and-routes.md +26 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/verify-module.sh +43 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Env / lint / node
|
|
2
|
+
|
|
3
|
+
## `.env.dev`
|
|
4
|
+
|
|
5
|
+
The CI build copies this to `.env` (`cp .env.dev .env`) before `nx build web --prod`. The values
|
|
6
|
+
are **project-specific** — collect them in step 1 (from an existing `.env*`, from
|
|
7
|
+
`apps/web/src/environments/environment.prod.ts`, or by prompting). Do not commit a real `.env`
|
|
8
|
+
(only `.env.dev`); `.env` should already be git-ignored by the base.
|
|
9
|
+
|
|
10
|
+
Template (replace the `{{...}}` placeholders):
|
|
11
|
+
|
|
12
|
+
```dotenv
|
|
13
|
+
APP_VERSION={{APP_VERSION}}
|
|
14
|
+
|
|
15
|
+
API_URL={{API_URL}}
|
|
16
|
+
SITE_URL={{SITE_URL}}
|
|
17
|
+
MULTIMEDIA_URL={{MULTIMEDIA_URL}}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Shape of the values, using `<museum>` for the client's own slug — these are patterns to follow, not
|
|
21
|
+
values to paste:
|
|
22
|
+
|
|
23
|
+
```dotenv
|
|
24
|
+
APP_VERSION=0.0.2
|
|
25
|
+
|
|
26
|
+
API_URL=https://muza-api.<museum-domain>/
|
|
27
|
+
SITE_URL=https://mobilems-muzeum-<museum>-qa.smartsoft.biz.pl/
|
|
28
|
+
MULTIMEDIA_URL=https://muza-serwer-cdn.<museum-domain>/
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`SITE_URL` is the one to get right: it drives `og:url`, `sitemap.xml` and `robots.txt`, so a value
|
|
32
|
+
carried over from another project ships that project's domain in this app's SEO. Pass this project's
|
|
33
|
+
expected host as the third argument to `verify-devops.sh` to have it checked.
|
|
34
|
+
|
|
35
|
+
These keys map to the `/env.js` runtime-env template that `mobilems-project-init` sets up in
|
|
36
|
+
`server.ts` (`API_URL`/`SITE_URL`/`APP_VERSION` are the base set; `MULTIMEDIA_URL` is the MobilEMS
|
|
37
|
+
addition). If a project is missing `MULTIMEDIA_URL` handling in `server.ts`, that's a project-init
|
|
38
|
+
gap — flag it, don't fix it here.
|
|
39
|
+
|
|
40
|
+
## `.eslintignore`
|
|
41
|
+
|
|
42
|
+
```gitignore
|
|
43
|
+
node_modules
|
|
44
|
+
**/tailwind.config.js
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## `.nvmrc`
|
|
48
|
+
|
|
49
|
+
A single line with the node version from step 1 — keep it identical to the Dockerfile base image
|
|
50
|
+
and every workflow `node-version`:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
{{NODE_VERSION}}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If a project already has a `.nvmrc` with a different version (e.g. `24.16.0` vs the team's
|
|
57
|
+
`24.5.0`), treat the existing value as the source of truth for `{{NODE_VERSION}}` unless the user
|
|
58
|
+
asks to bump it — then thread that one version everywhere and re-run `verify-devops.sh` to confirm
|
|
59
|
+
consistency.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# `.github` hygiene — agent config does not live in `.github`
|
|
2
|
+
|
|
3
|
+
`.github/` is for GitHub itself: **workflows, composite actions, and GitHub's own metadata**
|
|
4
|
+
(`CODEOWNERS`, issue/PR templates, `dependabot.yml`, `git-commit-instructions.md`).
|
|
5
|
+
|
|
6
|
+
Agent configuration targets the **Claude Code architecture** and belongs under `.claude/`:
|
|
7
|
+
|
|
8
|
+
| Artifact | Location |
|
|
9
|
+
| ------------------ | -------------------------------------------------------------- |
|
|
10
|
+
| Skills | `.claude/skills/<name>/SKILL.md` (+ its references/scripts) |
|
|
11
|
+
| Subagents | `.claude/agents/<name>.md` |
|
|
12
|
+
| Prompts / commands | `.claude/commands/<name>.md` |
|
|
13
|
+
| Settings, plugins | `.claude/settings.json` (`enabledPlugins`, permissions, hooks) |
|
|
14
|
+
|
|
15
|
+
Scaffolding tools drop agent config into `.github/` in the GitHub-Copilot layout instead — Nx's AI
|
|
16
|
+
setup writes `.github/skills/`, `.github/agents/`, `.github/prompts/`; other tools add
|
|
17
|
+
`.github/instructions/`, `.github/chatmodes/`, `.github/copilot-instructions.md`. Those files are not
|
|
18
|
+
read by Claude Code, so they are inert at best. At worst they are a second, stale copy of something a
|
|
19
|
+
plugin already provides: the `nx-*` skill family and `monitor-ci` (with its `ci-monitor-subagent`
|
|
20
|
+
agent and `monitor-ci.prompt.md`) ship with `nx@nx-claude-plugins`, so a project that enables that
|
|
21
|
+
plugin and keeps the `.github` copies maintains two versions that drift apart, with the unused one
|
|
22
|
+
invisible in review.
|
|
23
|
+
|
|
24
|
+
## Rule
|
|
25
|
+
|
|
26
|
+
For every entry under `.github/{skills,agents,prompts,instructions,chatmodes}` and
|
|
27
|
+
`.github/copilot-instructions.md`:
|
|
28
|
+
|
|
29
|
+
| Case | Action |
|
|
30
|
+
| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| **Duplicate** of a plugin enabled in `.claude/settings.json` (`nx@nx-claude-plugins`, `smart@smartsoft`, `flow@*`, `mobilems@mobilems`) | **Delete** — the plugin is the single source |
|
|
32
|
+
| **Bespoke** to this project (no plugin ships it) | **Move** into `.claude/` per the table above, converting to the Claude Code layout (`.agent.md` / `.prompt.md` suffixes become plain `<name>.md`) |
|
|
33
|
+
| GitHub's own metadata (`workflows`, `actions`, `CODEOWNERS`, templates, `dependabot.yml`, `git-commit-instructions.md`) | **Keep** |
|
|
34
|
+
|
|
35
|
+
Deciding "duplicate": compare the skill/agent name against the plugins enabled in
|
|
36
|
+
`.claude/settings.json`. When a name is ambiguous, diff it against the plugin's copy under
|
|
37
|
+
`node_modules` / the plugin cache; if it is materially customized, treat it as bespoke and move it.
|
|
38
|
+
|
|
39
|
+
## How to apply
|
|
40
|
+
|
|
41
|
+
1. List what is there — do not assume:
|
|
42
|
+
```bash
|
|
43
|
+
ls -d .github/skills .github/agents .github/prompts .github/instructions \
|
|
44
|
+
.github/chatmodes .github/copilot-instructions.md 2>/dev/null
|
|
45
|
+
```
|
|
46
|
+
2. Classify each entry with the table above and **show the user the plan** (delete list + move list)
|
|
47
|
+
before touching anything. Deleting files is not reversible from the skill's side — ask.
|
|
48
|
+
3. Apply with `git mv` / `git rm` so the history follows the move.
|
|
49
|
+
4. If anything moved into `.claude/`, name it in the final summary — a project-local skill or agent
|
|
50
|
+
is a candidate for upstreaming into a plugin.
|
|
51
|
+
5. Leave `.github` containing only `workflows/`, `actions/`, and GitHub metadata.
|
|
52
|
+
|
|
53
|
+
> If the plugin that duplicates a deleted skill is **not** enabled in `.claude/settings.json`, enable
|
|
54
|
+
> it there (`nx@nx-claude-plugins` for the Nx skills) instead of keeping the `.github` copy.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Update tooling
|
|
2
|
+
|
|
3
|
+
`npm run update` refreshes the `@smartsoft001*` packages to `@latest`, syncs the smartsoft Claude
|
|
4
|
+
plugins, and re-merges their permissions. This is generic across all museum projects — write it
|
|
5
|
+
verbatim.
|
|
6
|
+
|
|
7
|
+
## `tools/scripts/update.js`
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
// Clear npm cache with force
|
|
15
|
+
console.log('Clearing npm cache...');
|
|
16
|
+
try {
|
|
17
|
+
execSync('npm cache clean --force', { stdio: 'inherit' });
|
|
18
|
+
console.log('npm cache cleared successfully');
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.error('Failed to clear npm cache:', error.message);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Read package.json
|
|
24
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
25
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
26
|
+
|
|
27
|
+
// Get all @smartsoft001 packages
|
|
28
|
+
const dependencies = packageJson.dependencies || {};
|
|
29
|
+
const devDependencies = packageJson.devDependencies || {};
|
|
30
|
+
const allDeps = { ...dependencies, ...devDependencies };
|
|
31
|
+
|
|
32
|
+
const smartsoftPackages = Object.keys(allDeps).filter((pkg) =>
|
|
33
|
+
pkg.startsWith('@smartsoft001'),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
if (smartsoftPackages.length === 0) {
|
|
37
|
+
console.log('No @smartsoft001 packages found');
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log('Found @smartsoft001 packages:', smartsoftPackages);
|
|
42
|
+
|
|
43
|
+
// Update all packages with single npm install command
|
|
44
|
+
const packagesWithLatest = smartsoftPackages
|
|
45
|
+
.map((pkg) => `${pkg}@latest`)
|
|
46
|
+
.join(' ');
|
|
47
|
+
console.log(`Updating all @smartsoft001 packages to latest...`);
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
execSync(`npm install ${packagesWithLatest}`, { stdio: 'inherit' });
|
|
51
|
+
console.log('All @smartsoft001 packages updated successfully!');
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Failed to update packages:', error.message);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
execSync(`claude plugin update smart@smartsoft --scope project || true`, {
|
|
58
|
+
stdio: 'inherit',
|
|
59
|
+
});
|
|
60
|
+
execSync(`claude plugin update flow@smart-pro --scope project || true`, {
|
|
61
|
+
stdio: 'inherit',
|
|
62
|
+
});
|
|
63
|
+
console.log('Claude plugins updated successfully!');
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Failed to update Claude plugins:', error.message);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
execSync(
|
|
70
|
+
`node node_modules/@smartsoft001/claude-plugins/plugins/smart/.claude-plugin/merge-permissions.js `,
|
|
71
|
+
{
|
|
72
|
+
stdio: 'inherit',
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
execSync(
|
|
76
|
+
`node node_modules/@smartsoft001/pro-claude-plugins/plugins/flow/.claude-plugin/merge-permissions.js `,
|
|
77
|
+
{
|
|
78
|
+
stdio: 'inherit',
|
|
79
|
+
},
|
|
80
|
+
);
|
|
81
|
+
console.log('Claude plugin permissions merged successfully!');
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error('Failed to merge Claude plugin permissions:', error.message);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.log('Update complete!');
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## `package.json` scripts
|
|
90
|
+
|
|
91
|
+
Merge these into the existing `scripts` block (keep any scripts already there):
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"scripts": {
|
|
96
|
+
"update": "node ./tools/scripts/update.js",
|
|
97
|
+
"postinstall": "husky install"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
- `update` — the script above.
|
|
103
|
+
- `postinstall: husky install` — installs the Husky git hooks (`.husky/commit-msg`, `pre-push`,
|
|
104
|
+
`post-merge`). Some projects use `"prepare": "husky install"` instead; if `prepare` already runs
|
|
105
|
+
`husky install`, leave it and do **not** add a duplicate `postinstall`.
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
# GitHub Actions
|
|
2
|
+
|
|
3
|
+
Write each file under `.github/`. `{{NODE_VERSION}}` = the node version from step 1 (keep it
|
|
4
|
+
identical to `.nvmrc` and the Dockerfile). All client-specific values are GitHub Actions
|
|
5
|
+
**secrets** — the files contain only `${{ secrets.* }}` references, never literals.
|
|
6
|
+
|
|
7
|
+
The branch model: `development` → CI build + Docker push + dispatch QA deploy; `main` →
|
|
8
|
+
push-to-client (GitLab mirror + SSH production deploy).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## `.github/actions/cached-deps/action.yml`
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
name: 'Get & Cache Dependencies'
|
|
16
|
+
description: 'Get the dependencies (via npm) and cache them.'
|
|
17
|
+
inputs:
|
|
18
|
+
caching:
|
|
19
|
+
description: 'Whether to cache dependencies or not.'
|
|
20
|
+
required: false
|
|
21
|
+
default: 'true'
|
|
22
|
+
node-version:
|
|
23
|
+
description: 'Node version.'
|
|
24
|
+
required: false
|
|
25
|
+
default: '18'
|
|
26
|
+
outputs:
|
|
27
|
+
used-cache:
|
|
28
|
+
description: 'Whether the cache was used.'
|
|
29
|
+
value: ${{ steps.install.outputs.cache }}
|
|
30
|
+
runs:
|
|
31
|
+
using: 'composite'
|
|
32
|
+
steps:
|
|
33
|
+
- name: Install NodeJS
|
|
34
|
+
uses: actions/setup-node@v3
|
|
35
|
+
with:
|
|
36
|
+
node-version: ${{ inputs.node-version }}
|
|
37
|
+
- name: Cache dependencies
|
|
38
|
+
if: inputs.caching == 'true'
|
|
39
|
+
id: cache
|
|
40
|
+
uses: actions/cache@v3
|
|
41
|
+
with:
|
|
42
|
+
path: node_modules
|
|
43
|
+
key: deps-node-modules-${{ hashFiles('**/package-lock.json') }}
|
|
44
|
+
- name: Install dependencies
|
|
45
|
+
id: install
|
|
46
|
+
if: steps.cache.outputs.cache-hit != 'true' || inputs.caching != 'true'
|
|
47
|
+
run: |
|
|
48
|
+
npm ci
|
|
49
|
+
echo "cache='${{ inputs.caching }}'" >> $GITHUB_OUTPUT
|
|
50
|
+
shell: bash
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## `.github/workflows/development.yml`
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
name: Development
|
|
59
|
+
on:
|
|
60
|
+
workflow_dispatch:
|
|
61
|
+
push:
|
|
62
|
+
branches:
|
|
63
|
+
- development
|
|
64
|
+
|
|
65
|
+
jobs:
|
|
66
|
+
main:
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
permissions:
|
|
69
|
+
contents: 'write'
|
|
70
|
+
actions: 'read'
|
|
71
|
+
steps:
|
|
72
|
+
- name: Checkout git
|
|
73
|
+
uses: actions/checkout@v6
|
|
74
|
+
with:
|
|
75
|
+
fetch-depth: 0
|
|
76
|
+
- name: Setup node
|
|
77
|
+
uses: actions/setup-node@v6
|
|
78
|
+
with:
|
|
79
|
+
node-version: { { NODE_VERSION } }
|
|
80
|
+
cache: 'npm'
|
|
81
|
+
- name: Load & cache dependencies
|
|
82
|
+
id: cache-deps
|
|
83
|
+
uses: ./.github/actions/cached-deps
|
|
84
|
+
with:
|
|
85
|
+
node-version: '{{NODE_VERSION}}'
|
|
86
|
+
- name: Set version
|
|
87
|
+
run: npm version minor --no-git-tag-version
|
|
88
|
+
- id: package
|
|
89
|
+
run: |
|
|
90
|
+
echo "::set-output name=version::$(node -p "require('./package.json').version")"
|
|
91
|
+
- name: Copy .env.dev to .env
|
|
92
|
+
run: cp .env.dev .env
|
|
93
|
+
- name: Build NX
|
|
94
|
+
run: |
|
|
95
|
+
npx nx build web --prod --verbose
|
|
96
|
+
- name: Set up Docker Buildx
|
|
97
|
+
uses: docker/setup-buildx-action@v3
|
|
98
|
+
- name: Login to DockerHub
|
|
99
|
+
uses: docker/login-action@v3
|
|
100
|
+
with:
|
|
101
|
+
username: ${{ secrets.GITLAB_USERNAME }}
|
|
102
|
+
password: ${{ secrets.GITLAB_TOKEN }}
|
|
103
|
+
registry: dockerregistry.mobilems.pl
|
|
104
|
+
- name: Build and push docker
|
|
105
|
+
run: |
|
|
106
|
+
docker buildx build -f apps/web/Dockerfile --build-arg VERSION=${{ steps.package.outputs.version }} -t dockerregistry.mobilems.pl/${{ secrets.GITLAB_REPO_PATH }}:${{ steps.package.outputs.version }} --push .
|
|
107
|
+
- name: Commit git
|
|
108
|
+
uses: stefanzweifel/git-auto-commit-action@v4
|
|
109
|
+
with:
|
|
110
|
+
commit_message: 'build(github): publish ${{ steps.package.outputs.version }} [skip ci]'
|
|
111
|
+
tagging_message: 'v${{ steps.package.outputs.version }}'
|
|
112
|
+
|
|
113
|
+
- uses: lasith-kg/dispatch-workflow@v1
|
|
114
|
+
id: workflow-dispatch
|
|
115
|
+
name: 'Deploy to qa'
|
|
116
|
+
with:
|
|
117
|
+
dispatch-method: workflow_dispatch
|
|
118
|
+
repo: ${{ secrets.DEPLOY_REPO }}
|
|
119
|
+
owner: smartsoft001
|
|
120
|
+
ref: main
|
|
121
|
+
workflow: deploy.yml
|
|
122
|
+
token: ${{ secrets.DEPLOY_TOKEN }}
|
|
123
|
+
workflow-inputs: |
|
|
124
|
+
{
|
|
125
|
+
"version": "${{ steps.package.outputs.version }}",
|
|
126
|
+
"env": "qa"
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## `.github/workflows/e2e.yml`
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
name: E2E Tests
|
|
136
|
+
on:
|
|
137
|
+
workflow_dispatch:
|
|
138
|
+
|
|
139
|
+
jobs:
|
|
140
|
+
e2e:
|
|
141
|
+
runs-on: ubuntu-latest
|
|
142
|
+
permissions:
|
|
143
|
+
contents: 'read'
|
|
144
|
+
steps:
|
|
145
|
+
- name: Checkout git
|
|
146
|
+
uses: actions/checkout@v6
|
|
147
|
+
with:
|
|
148
|
+
fetch-depth: 0
|
|
149
|
+
- name: Setup node
|
|
150
|
+
uses: actions/setup-node@v6
|
|
151
|
+
with:
|
|
152
|
+
node-version: { { NODE_VERSION } }
|
|
153
|
+
cache: 'npm'
|
|
154
|
+
- name: Load & cache dependencies
|
|
155
|
+
id: cache-deps
|
|
156
|
+
uses: ./.github/actions/cached-deps
|
|
157
|
+
with:
|
|
158
|
+
node-version: '{{NODE_VERSION}}'
|
|
159
|
+
- name: Copy .env.dev to .env
|
|
160
|
+
run: cp .env.dev .env
|
|
161
|
+
- name: Build NX
|
|
162
|
+
run: npx nx build web --prod --verbose
|
|
163
|
+
- name: Install Playwright browsers
|
|
164
|
+
run: npx playwright install --with-deps chromium webkit
|
|
165
|
+
- name: Run E2E tests
|
|
166
|
+
run: npx nx e2e web-e2e
|
|
167
|
+
- name: Upload Playwright report
|
|
168
|
+
if: always()
|
|
169
|
+
uses: actions/upload-artifact@v4
|
|
170
|
+
with:
|
|
171
|
+
name: playwright-report
|
|
172
|
+
path: |
|
|
173
|
+
dist/.playwright/apps/web-e2e/
|
|
174
|
+
apps/web-e2e/test-results/
|
|
175
|
+
lighthouse-results/
|
|
176
|
+
audit-results/
|
|
177
|
+
retention-days: 30
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## `.github/workflows/push-to-client.yml`
|
|
183
|
+
|
|
184
|
+
```yaml
|
|
185
|
+
name: Push to client
|
|
186
|
+
|
|
187
|
+
on:
|
|
188
|
+
workflow_dispatch:
|
|
189
|
+
push:
|
|
190
|
+
branches:
|
|
191
|
+
- main
|
|
192
|
+
|
|
193
|
+
jobs:
|
|
194
|
+
push:
|
|
195
|
+
runs-on: ubuntu-latest
|
|
196
|
+
steps:
|
|
197
|
+
- name: Checkout code
|
|
198
|
+
uses: actions/checkout@v5.0.0
|
|
199
|
+
with:
|
|
200
|
+
fetch-depth: 0
|
|
201
|
+
|
|
202
|
+
- name: Set up user
|
|
203
|
+
run: |
|
|
204
|
+
git config --global user.name "${{ secrets.GITLAB_USERNAME }}"
|
|
205
|
+
git config --global user.email "${{ secrets.GITLAB_USERNAME }}"
|
|
206
|
+
|
|
207
|
+
- name: Add remote and push
|
|
208
|
+
run: |
|
|
209
|
+
git remote add client https://${{ secrets.GITLAB_USERNAME }}:${{ secrets.GITLAB_TOKEN }}@git.mobilems.pl/${{ secrets.GITLAB_REPO_PATH }}
|
|
210
|
+
git push client --force
|
|
211
|
+
|
|
212
|
+
- name: Get package version
|
|
213
|
+
id: package
|
|
214
|
+
run: |
|
|
215
|
+
echo "version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
|
|
216
|
+
|
|
217
|
+
- name: Install OpenVPN
|
|
218
|
+
run: sudo apt-get update && sudo apt-get install -y openvpn
|
|
219
|
+
|
|
220
|
+
- name: Create OpenVPN config file
|
|
221
|
+
run: echo "${{ secrets.OPENVPN_CONFIG }}" > /tmp/openvpn.ovpn
|
|
222
|
+
|
|
223
|
+
- name: Connect to OpenVPN
|
|
224
|
+
uses: kota65535/github-openvpn-connect-action@v3
|
|
225
|
+
with:
|
|
226
|
+
config_file: /tmp/openvpn.ovpn
|
|
227
|
+
|
|
228
|
+
- name: Deploy to production server
|
|
229
|
+
uses: appleboy/ssh-action@v1
|
|
230
|
+
with:
|
|
231
|
+
host: ${{ secrets.PROD_SSH_HOST }}
|
|
232
|
+
username: ${{ secrets.PROD_SSH_USERNAME }}
|
|
233
|
+
key: ${{ secrets.PROD_SSH_KEY }}
|
|
234
|
+
passphrase: ${{ secrets.PROD_SSH_PASSPHRASE }}
|
|
235
|
+
port: ${{ secrets.PROD_SSH_PORT }}
|
|
236
|
+
envs: VERSION,DOCKER_USER,DOCKER_PASS
|
|
237
|
+
script: |
|
|
238
|
+
cd ~/front
|
|
239
|
+
cp docker-front-stack.yml.template docker-front-stack.yml
|
|
240
|
+
sed -i "s/\$VERSION/$VERSION/g" docker-front-stack.yml
|
|
241
|
+
docker logout dockerregistry.mobilems.pl || true
|
|
242
|
+
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS" dockerregistry.mobilems.pl
|
|
243
|
+
docker stack deploy -c docker-front-stack.yml front --with-registry-auth
|
|
244
|
+
docker logout dockerregistry.mobilems.pl
|
|
245
|
+
env:
|
|
246
|
+
VERSION: ${{ steps.package.outputs.version }}
|
|
247
|
+
DOCKER_USER: ${{ secrets.GITLAB_USERNAME }}
|
|
248
|
+
DOCKER_PASS: ${{ secrets.GITLAB_TOKEN }}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## `.github/workflows/ai-plan.yml`
|
|
254
|
+
|
|
255
|
+
```yaml
|
|
256
|
+
name: AI Plan
|
|
257
|
+
|
|
258
|
+
on:
|
|
259
|
+
repository_dispatch:
|
|
260
|
+
types: [linear-ai-plan]
|
|
261
|
+
workflow_dispatch:
|
|
262
|
+
inputs:
|
|
263
|
+
issue_id:
|
|
264
|
+
description: 'Linear issue identifier (e.g. GAL-123)'
|
|
265
|
+
required: true
|
|
266
|
+
type: string
|
|
267
|
+
|
|
268
|
+
concurrency:
|
|
269
|
+
group: ai-plan-${{ github.event.client_payload.issue_id || github.event.inputs.issue_id }}
|
|
270
|
+
cancel-in-progress: false
|
|
271
|
+
|
|
272
|
+
jobs:
|
|
273
|
+
plan:
|
|
274
|
+
uses: smartsoft001/github-actions-shared/.github/workflows/ai-plan.yml@v1
|
|
275
|
+
with:
|
|
276
|
+
issue_id: ${{ github.event.client_payload.issue_id || github.event.inputs.issue_id }}
|
|
277
|
+
secrets:
|
|
278
|
+
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
279
|
+
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## `.github/workflows/ai-impl.yml`
|
|
285
|
+
|
|
286
|
+
```yaml
|
|
287
|
+
name: AI Impl
|
|
288
|
+
|
|
289
|
+
on:
|
|
290
|
+
repository_dispatch:
|
|
291
|
+
types: [linear-ai-impl]
|
|
292
|
+
workflow_dispatch:
|
|
293
|
+
inputs:
|
|
294
|
+
issue_id:
|
|
295
|
+
description: 'Linear issue identifier (e.g. GAL-123). The branch will be created with this exact name.'
|
|
296
|
+
required: true
|
|
297
|
+
type: string
|
|
298
|
+
base_branch:
|
|
299
|
+
description: 'Override PR target (default: development)'
|
|
300
|
+
required: false
|
|
301
|
+
type: string
|
|
302
|
+
ai_size:
|
|
303
|
+
description: 'Manual override for AI gate classification (S/M/L). Leave empty for default M.'
|
|
304
|
+
required: false
|
|
305
|
+
type: choice
|
|
306
|
+
default: ''
|
|
307
|
+
options: ['', 'S', 'M', 'L']
|
|
308
|
+
ai_complexity:
|
|
309
|
+
description: 'Manual override for AI gate classification (S/M/L). Leave empty for default M.'
|
|
310
|
+
required: false
|
|
311
|
+
type: choice
|
|
312
|
+
default: ''
|
|
313
|
+
options: ['', 'S', 'M', 'L']
|
|
314
|
+
|
|
315
|
+
concurrency:
|
|
316
|
+
group: ai-impl-${{ github.event.client_payload.issue_id || github.event.inputs.issue_id }}
|
|
317
|
+
cancel-in-progress: false
|
|
318
|
+
|
|
319
|
+
jobs:
|
|
320
|
+
impl:
|
|
321
|
+
uses: smartsoft001/github-actions-shared/.github/workflows/ai-impl.yml@v1
|
|
322
|
+
with:
|
|
323
|
+
issue_id: ${{ github.event.client_payload.issue_id || github.event.inputs.issue_id }}
|
|
324
|
+
base_branch: ${{ github.event.client_payload.base_branch || github.event.inputs.base_branch || 'development' }}
|
|
325
|
+
ai_size: ${{ github.event.client_payload.ai_size || github.event.inputs.ai_size || '' }}
|
|
326
|
+
ai_complexity: ${{ github.event.client_payload.ai_complexity || github.event.inputs.ai_complexity || '' }}
|
|
327
|
+
secrets:
|
|
328
|
+
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
329
|
+
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## `.github/workflows/ci.yml.disabled`
|
|
335
|
+
|
|
336
|
+
Kept **disabled** (the `.disabled` suffix means GitHub never runs it). It's the Nx-Cloud template;
|
|
337
|
+
the team enables it by renaming to `ci.yml` and uncommenting the distribution line.
|
|
338
|
+
|
|
339
|
+
```yaml
|
|
340
|
+
name: CI
|
|
341
|
+
|
|
342
|
+
on:
|
|
343
|
+
push:
|
|
344
|
+
branches:
|
|
345
|
+
- main
|
|
346
|
+
pull_request:
|
|
347
|
+
|
|
348
|
+
permissions:
|
|
349
|
+
actions: read
|
|
350
|
+
contents: read
|
|
351
|
+
|
|
352
|
+
jobs:
|
|
353
|
+
main:
|
|
354
|
+
runs-on: ubuntu-latest
|
|
355
|
+
steps:
|
|
356
|
+
- uses: actions/checkout@v4
|
|
357
|
+
with:
|
|
358
|
+
filter: tree:0
|
|
359
|
+
fetch-depth: 0
|
|
360
|
+
|
|
361
|
+
# This enables task distribution via Nx Cloud
|
|
362
|
+
# Run this command as early as possible, before dependencies are installed
|
|
363
|
+
# Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun
|
|
364
|
+
# Uncomment this line to enable task distribution
|
|
365
|
+
# - run: npx nx start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="e2e-ci"
|
|
366
|
+
|
|
367
|
+
# Cache node_modules
|
|
368
|
+
- uses: actions/setup-node@v4
|
|
369
|
+
with:
|
|
370
|
+
node-version: { { NODE_VERSION } }
|
|
371
|
+
cache: 'npm'
|
|
372
|
+
|
|
373
|
+
- run: npm ci
|
|
374
|
+
- name: Install Playwright browsers
|
|
375
|
+
run: npx playwright install --with-deps chromium
|
|
376
|
+
|
|
377
|
+
# Prepend any command with "nx-cloud record --" to record its logs to Nx Cloud
|
|
378
|
+
# - run: npx nx-cloud record -- echo Hello World
|
|
379
|
+
- run: npx nx run-many -t lint test build e2e
|
|
380
|
+
# Nx Cloud recommends fixes for failures to help you get CI green faster. Learn more: https://nx.dev/ci/features/self-healing-ci
|
|
381
|
+
- run: npx nx fix-ci
|
|
382
|
+
if: always()
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
> Note: the reference's `ci.yml.disabled` pins Node 20. We thread `{{NODE_VERSION}}` here so the
|
|
386
|
+
> disabled template matches the rest of the project. If a project has a live `ci.yml` on a stale
|
|
387
|
+
> node version, report the drift and ask before changing it.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Required GitHub Actions secrets
|
|
392
|
+
|
|
393
|
+
Configured in the repo's **Settings → Secrets and variables → Actions** — never committed. Print
|
|
394
|
+
this table for the user after writing the workflows:
|
|
395
|
+
|
|
396
|
+
| Secret | Used by | Purpose |
|
|
397
|
+
| ------------------------- | --------------------------- | ------------------------------------------------- |
|
|
398
|
+
| `GITLAB_USERNAME` | development, push-to-client | Docker registry + GitLab mirror user |
|
|
399
|
+
| `GITLAB_TOKEN` | development, push-to-client | Docker registry + GitLab mirror token |
|
|
400
|
+
| `GITLAB_REPO_PATH` | development, push-to-client | Image path / GitLab repo path |
|
|
401
|
+
| `DEPLOY_REPO` | development | Companion `<project>-deploy` repo name |
|
|
402
|
+
| `DEPLOY_TOKEN` | development | Token to dispatch `deploy.yml` in the deploy repo |
|
|
403
|
+
| `OPENVPN_CONFIG` | push-to-client | OpenVPN `.ovpn` config content |
|
|
404
|
+
| `PROD_SSH_HOST` | push-to-client | Production server host |
|
|
405
|
+
| `PROD_SSH_USERNAME` | push-to-client | Production SSH user |
|
|
406
|
+
| `PROD_SSH_KEY` | push-to-client | Production SSH private key |
|
|
407
|
+
| `PROD_SSH_PASSPHRASE` | push-to-client | SSH key passphrase |
|
|
408
|
+
| `PROD_SSH_PORT` | push-to-client | SSH port |
|
|
409
|
+
| `CLAUDE_CODE_OAUTH_TOKEN` | ai-plan, ai-impl | Claude Code OAuth token |
|
|
410
|
+
| `LINEAR_API_KEY` | ai-plan, ai-impl | Linear API key |
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# detect-foundation.sh <workspace-root>
|
|
4
|
+
#
|
|
5
|
+
# Precondition gate for mobilems-devops-config. Confirms the target is a MobilEMS museum app whose
|
|
6
|
+
# app-runtime foundation (built by mobilems-project-init) is already in place — this skill only adds
|
|
7
|
+
# the ops/deployment layer on top, it does NOT set up SSR / translations / app.config.
|
|
8
|
+
# Portable to the macOS system bash (3.2) — no bash-4 features.
|
|
9
|
+
#
|
|
10
|
+
# Exit 0 → MobilEMS foundation detected, OK to add the ops layer.
|
|
11
|
+
# Exit 2 → bad workspace path.
|
|
12
|
+
# Exit 3 → not a MobilEMS foundation (tell the user to run /mobilems:mobilems-project-init first).
|
|
13
|
+
set -eu
|
|
14
|
+
|
|
15
|
+
ROOT="${1:?usage: detect-foundation.sh <workspace-root>}"
|
|
16
|
+
cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
|
|
17
|
+
|
|
18
|
+
fail_no_foundation() {
|
|
19
|
+
echo "NOT A MOBILEMS FOUNDATION: $1" >&2
|
|
20
|
+
echo "Run /mobilems:mobilems-project-init first (this skill only adds the ops/deployment layer)." >&2
|
|
21
|
+
exit 3
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# --- museum app + SSR foundation signals ---
|
|
25
|
+
[ -f apps/web/src/server.ts ] || fail_no_foundation "apps/web/src/server.ts missing (no SSR)"
|
|
26
|
+
[ -f package.json ] || fail_no_foundation "package.json missing"
|
|
27
|
+
grep -q "@smartsoft001-mobilems" package.json || fail_no_foundation "@smartsoft001-mobilems/* not in package.json"
|
|
28
|
+
grep -q "/api/translations" apps/web/src/server.ts || fail_no_foundation "/api/translations endpoint missing in server.ts"
|
|
29
|
+
[ -f apps/web/public/translations.json ] || fail_no_foundation "apps/web/public/translations.json missing"
|
|
30
|
+
|
|
31
|
+
echo "MOBILEMS FOUNDATION OK."
|
|
32
|
+
|
|
33
|
+
# --- report which ops artifacts are already present (informational; this skill fills the gaps) ---
|
|
34
|
+
have() { if [ -e "$1" ]; then echo " have: $1"; else echo " MISSING: $1"; fi; }
|
|
35
|
+
echo "Existing ops artifacts:"
|
|
36
|
+
have apps/web/Dockerfile
|
|
37
|
+
have .github/actions/cached-deps/action.yml
|
|
38
|
+
have .github/workflows/development.yml
|
|
39
|
+
have .github/workflows/e2e.yml
|
|
40
|
+
have .github/workflows/push-to-client.yml
|
|
41
|
+
have .github/workflows/ai-plan.yml
|
|
42
|
+
have .github/workflows/ai-impl.yml
|
|
43
|
+
have tools/scripts/update.js
|
|
44
|
+
have .env.dev
|
|
45
|
+
have .eslintignore
|
|
46
|
+
have .nvmrc
|
|
47
|
+
exit 0
|