@jenga-ai/agent 1.0.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 +201 -0
- package/README.md +340 -0
- package/agents/ai_engineer.md +113 -0
- package/agents/developer.md +236 -0
- package/agents/scrum-master.md +349 -0
- package/agents/scrutiny-agent.md +137 -0
- package/agents/solution-assessor.md +185 -0
- package/agents/tester.md +339 -0
- package/bin/jenga.js +70 -0
- package/hooks/copilot_session_end.sh +29 -0
- package/hooks/on_session_end.sh +238 -0
- package/hooks/prompt_router.sh +11 -0
- package/hooks/prompt_router_helper.js +52 -0
- package/hooks/session_end_helper.js +29 -0
- package/hooks/session_end_watcher.sh +24 -0
- package/lib/commands/attach.js +47 -0
- package/lib/commands/init.js +207 -0
- package/lib/commands/start.js +16 -0
- package/lib/commands/status.js +53 -0
- package/lib/config-schema.js +72 -0
- package/lib/inject-settings.js +61 -0
- package/lib/mirror.js +244 -0
- package/lib/resolve-project-dir.sh +47 -0
- package/mcp/execute-ticket/index.js +10 -0
- package/mcp/execute-ticket/package.json +5 -0
- package/mcp/help/index.js +79 -0
- package/mcp/help/package.json +14 -0
- package/mcp/router/README.md +19 -0
- package/mcp/router/embedder.js +23 -0
- package/mcp/router/index.js +204 -0
- package/mcp/router/matcher.js +87 -0
- package/mcp/router/package-lock.json +1048 -0
- package/mcp/router/package.json +11 -0
- package/mcp/router/skill-index.js +104 -0
- package/package.json +47 -0
- package/scripts/board_resolver.sh +46 -0
- package/scripts/e25_s01_extract_board_graph.py +292 -0
- package/scripts/e25_s01_generate_synthetic_board.py +90 -0
- package/scripts/measurement-10x.json +50 -0
- package/scripts/measurement-10x.txt +4 -0
- package/scripts/measurement-real.json +50 -0
- package/scripts/measurement-real.txt +4 -0
- package/scripts/postinstall.js +165 -0
- package/scripts/todo_cleanup.sh +22 -0
- package/scripts/todo_manager.sh +86 -0
- package/scripts/validate-board.sh +190 -0
- package/scripts/validate-story-format.sh +53 -0
- package/skills/brainstorm/SKILL.md +47 -0
- package/skills/btw/SKILL.md +42 -0
- package/skills/commit/SKILL.md +29 -0
- package/skills/commit/assets/user_instructions_template.md +22 -0
- package/skills/continue/SKILL.md +29 -0
- package/skills/convert/SKILL.md +124 -0
- package/skills/convert/convert_cli.py +235 -0
- package/skills/convert/tests/sample.csv +4 -0
- package/skills/convert/tests/sample.json +5 -0
- package/skills/convert/tests/sample.jsonl +3 -0
- package/skills/convert/tests/sample.yaml +18 -0
- package/skills/convert/tests/sample_obj.csv +2 -0
- package/skills/convert/tests/sample_obj.json +9 -0
- package/skills/deep-dive/SKILL.md +167 -0
- package/skills/do/SKILL.md +88 -0
- package/skills/do/assets/sender_template.json +12 -0
- package/skills/doc/SKILL.md +314 -0
- package/skills/doc/assets/path-objectives.yaml +38 -0
- package/skills/doc-sync/SKILL.md +167 -0
- package/skills/doc-sync/assets/default_excludes.txt +21 -0
- package/skills/doc-sync/assets/doc_targets.md +14 -0
- package/skills/dooo/SKILL.md +60 -0
- package/skills/error/SKILL.md +29 -0
- package/skills/evaluate/SKILL.md +45 -0
- package/skills/evaluate/assets/evaluation_invokation_template.yml +3 -0
- package/skills/evaluate/assets/evaluation_rapport_template.md +24 -0
- package/skills/examplify/SKILL.md +42 -0
- package/skills/help/SKILL.md +36 -0
- package/skills/improve/SKILL.md +55 -0
- package/skills/index/scripts/board-index +4 -0
- package/skills/index/scripts/board_index.py +615 -0
- package/skills/index/scripts/smoke_test.sh +86 -0
- package/skills/init/SKILL.md +44 -0
- package/skills/init/assets/.gitignore_template +15 -0
- package/skills/init/assets/PROJECT_SUMMARY_template.md +13 -0
- package/skills/init/assets/directory_structure.txt +13 -0
- package/skills/init/assets/test-config_template.json +4 -0
- package/skills/init/assets/workflow_template.json +30 -0
- package/skills/init/scripts/init.sh +48 -0
- package/skills/jbp/SKILL.md +25 -0
- package/skills/jenga/SKILL.md +68 -0
- package/skills/lgtm/SKILL.md +21 -0
- package/skills/mirror-public/SKILL.md +237 -0
- package/skills/mirror-public/assets/config.json +5 -0
- package/skills/mirror-public/scripts/mirror.sh +374 -0
- package/skills/pi-plan/SKILL.md +62 -0
- package/skills/pi-plan/assets/epic.json +7 -0
- package/skills/pi-plan/assets/story_template.md +18 -0
- package/skills/proceed/SKILL.md +29 -0
- package/skills/publish/SKILL.md +351 -0
- package/skills/publish/adapters/droplet.md +200 -0
- package/skills/publish/adapters/mobile-ios.md +114 -0
- package/skills/publish/adapters/npm-ci.md +223 -0
- package/skills/publish/adapters/npm.md +121 -0
- package/skills/publish/assets/ExportOptions.plist.template +19 -0
- package/skills/publish/assets/ci-contract.md +111 -0
- package/skills/publish/assets/ownership-matrix.md +17 -0
- package/skills/publish/assets/publish.example.json +85 -0
- package/skills/publish/assets/publish.example.npm-ci.json +40 -0
- package/skills/publish/assets/publish.example.npm.json +41 -0
- package/skills/publish/assets/secrets-guide.md +104 -0
- package/skills/publish/schemas/fixtures/npm-ci-minimal.json +17 -0
- package/skills/publish/schemas/fixtures/npm-ci-with-empty-secrets.json +18 -0
- package/skills/publish/schemas/fixtures/npm-ci-with-workflow-path.json +18 -0
- package/skills/publish/schemas/publish.schema.json +428 -0
- package/skills/publish/scripts/check_target_config.sh +96 -0
- package/skills/publish/scripts/droplet_pipeline.sh +208 -0
- package/skills/publish/scripts/generate_release_notes.sh +200 -0
- package/skills/publish/scripts/ios_pipeline.sh +486 -0
- package/skills/publish/scripts/npm_ci_pipeline.sh +225 -0
- package/skills/publish/scripts/npm_pipeline.sh +249 -0
- package/skills/publish/scripts/publish_common.sh +253 -0
- package/skills/publish/scripts/publish_deploy.sh +538 -0
- package/skills/publish/scripts/reconcile_tags.sh +135 -0
- package/skills/publish/scripts/run_gates.sh +616 -0
- package/skills/publish/scripts/setup_wizard.sh +394 -0
- package/skills/publish/scripts/show_history.sh +95 -0
- package/skills/publish/scripts/suggest_semver_bump.sh +105 -0
- package/skills/publish/scripts/validate_config.sh +163 -0
- package/skills/publish/scripts/validate_droplet_env.sh +45 -0
- package/skills/publish/scripts/validate_ios_env.sh +68 -0
- package/skills/publish/scripts/validate_npm_ci_env.sh +71 -0
- package/skills/publish/scripts/validate_npm_env.sh +22 -0
- package/skills/publish/scripts/write_ledger_entry.sh +126 -0
- package/skills/publish/wizards/droplet.md +275 -0
- package/skills/publish/wizards/mobile-ios.md +157 -0
- package/skills/publish/wizards/npm-ci.md +240 -0
- package/skills/publish/wizards/npm.md +224 -0
- package/skills/reconcile/SKILL.md +93 -0
- package/skills/reconcile/assets/report_format.md +44 -0
- package/skills/reconcile-origin/SKILL.md +75 -0
- package/skills/reconcile-origin/scripts/reconcile-origin.sh +372 -0
- package/skills/redo/SKILL.md +70 -0
- package/skills/route/SKILL.md +180 -0
- package/skills/self-sync/SKILL.md +73 -0
- package/skills/self-sync/scripts/run.js +136 -0
- package/skills/skillify/SKILL.md +68 -0
- package/skills/skillify/assets/init-new/SKILL.md +35 -0
- package/skills/skillify/assets/init-new/assets/.gitignore_template +15 -0
- package/skills/skillify/assets/init-new/assets/PROJECT_SUMMARY_template.md +13 -0
- package/skills/skillify/assets/init-new/assets/directory_structure.txt +10 -0
- package/skills/skillify/assets/init-new/assets/test-config_template.json +4 -0
- package/skills/skillify/assets/init-new/assets/workflow_template.json +17 -0
- package/skills/skillify/assets/init-new/scripts/init.sh +48 -0
- package/skills/skillify/assets/init-old/SKILL.md +124 -0
- package/skills/spinoff/SKILL.md +48 -0
- package/skills/status/SKILL.md +33 -0
- package/skills/status/assets/output_format.md +41 -0
- package/skills/todo/SKILL.md +46 -0
- package/skills/todo/assets/todo_handoff_template.md +22 -0
- package/skills/todo/assets/todo_template.md +3 -0
- package/skills/train/SKILL.md +116 -0
- package/skills/train/assets/dashboard-templates/classifiers.html +106 -0
- package/skills/train/assets/dashboard-templates/nlp.html +102 -0
- package/skills/train/assets/dashboard-templates/transformers.html +98 -0
- package/skills/train/assets/results-parsers/__init__.py +9 -0
- package/skills/train/assets/results-parsers/classifiers.py +84 -0
- package/skills/train/assets/results-parsers/nlp.py +88 -0
- package/skills/train/assets/results-parsers/reporter.py +154 -0
- package/skills/train/assets/results-parsers/transformers.py +120 -0
- package/skills/train/train_cli.py +786 -0
- package/templates/EXECUTION_PLAN_TEMPLATE.md +43 -0
- package/templates/EXECUTION_SUMMARY_TEMPLATE.md +50 -0
- package/templates/JENGA_CONFIG_TEMPLATE.json +23 -0
- package/templates/PROBLEM_RAPPORT_TEMPLATE.md +88 -0
- package/templates/SCRUM_BOARD_SCHEMA.md +311 -0
- package/templates/SKILL.md +16 -0
- package/templates/SKILL_TEMPLATE.md +28 -0
- package/templates/USER_INSTRUCTIONS_TEMPLATE.md +22 -0
- package/templates/copilot-instructions.md.tpl +55 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Publish secrets guide
|
|
2
|
+
|
|
3
|
+
## Never do this
|
|
4
|
+
|
|
5
|
+
Never commit secrets, tokens, certificates, API keys, or `.env` files into git.
|
|
6
|
+
Use gitignored local files or secret stores instead.
|
|
7
|
+
|
|
8
|
+
```gitignore
|
|
9
|
+
.env
|
|
10
|
+
.env.*
|
|
11
|
+
*.local
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
If a value is sensitive, keep the real value outside the repository and store only its environment-variable name in `publish.json`.
|
|
15
|
+
|
|
16
|
+
## Local development
|
|
17
|
+
|
|
18
|
+
For local-only setup, keep secrets in a gitignored `.env` file or shell profile and load them before running `/publish`.
|
|
19
|
+
If you already use `direnv`, prefer a gitignored `.envrc` or sourced env file so the variables are loaded automatically when you enter the project.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
export APP_STORE_CONNECT_API_KEY_ID=ABC123XYZ
|
|
25
|
+
export IOS_PROVISIONING_PROFILE_UUID=11111111-2222-3333-4444-555555555555
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## macOS Keychain
|
|
29
|
+
|
|
30
|
+
Use the macOS Keychain when you do not want long-lived secret values sitting in plain text files.
|
|
31
|
+
Store a secret:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
security add-generic-password -a "$USER" -s publish.app-store-connect.api-key-id -w '<value>'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Read it later into an environment variable:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
export APP_STORE_CONNECT_API_KEY_ID="$(security find-generic-password -a "$USER" -s publish.app-store-connect.api-key-id -w)"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
You can repeat the same pattern for provisioning-profile references or other deploy credentials.
|
|
44
|
+
|
|
45
|
+
## GitHub Actions / CI secrets
|
|
46
|
+
|
|
47
|
+
Store shared secrets in repository or organization secrets, then map them to job-level environment variables.
|
|
48
|
+
Reference them in workflow files with `${{ secrets.NAME }}`.
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
env:
|
|
54
|
+
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
|
|
55
|
+
IOS_PROVISIONING_PROFILE_UUID: ${{ secrets.IOS_PROVISIONING_PROFILE_UUID }}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use repository secrets for project-specific values and organization secrets for centrally managed credentials that multiple repositories share.
|
|
59
|
+
|
|
60
|
+
## Droplet (GitHub Actions → SSH)
|
|
61
|
+
|
|
62
|
+
The `droplet` adapter reads all SSH credentials from GitHub Actions secrets at workflow runtime. Your `publish.json` stores only the *names* of the secrets — never the values.
|
|
63
|
+
|
|
64
|
+
### Required secrets
|
|
65
|
+
|
|
66
|
+
| Secret name | Value |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `DROPLET_SSH_HOST` | IP address or FQDN of your target host |
|
|
69
|
+
| `DROPLET_SSH_USER` | SSH login username (e.g. `ubuntu`, `deploy`) |
|
|
70
|
+
| `DROPLET_SSH_PORT` | SSH port number (usually `22`) |
|
|
71
|
+
| `DROPLET_SSH_KEY` | Contents of the private key used to SSH into the host |
|
|
72
|
+
| `DROPLET_KNOWN_HOSTS` | Output of `ssh-keyscan -H <host>` |
|
|
73
|
+
|
|
74
|
+
### How to add secrets
|
|
75
|
+
|
|
76
|
+
Via the `gh` CLI (recommended):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
gh secret set DROPLET_SSH_HOST --body "1.2.3.4" --repo owner/myrepo
|
|
80
|
+
gh secret set DROPLET_SSH_USER --body "deploy" --repo owner/myrepo
|
|
81
|
+
gh secret set DROPLET_SSH_PORT --body "22" --repo owner/myrepo
|
|
82
|
+
gh secret set DROPLET_SSH_KEY < ~/.ssh/id_deploy --repo owner/myrepo
|
|
83
|
+
ssh-keyscan -H 1.2.3.4 | gh secret set DROPLET_KNOWN_HOSTS --repo owner/myrepo
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Via the GitHub UI: **Repository → Settings → Secrets and variables → Actions → New repository secret**.
|
|
87
|
+
|
|
88
|
+
### Generating the SSH key pair
|
|
89
|
+
|
|
90
|
+
If you don't have a dedicated deploy key:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
ssh-keygen -t ed25519 -C "deploy@myrepo" -f ~/.ssh/id_deploy -N ""
|
|
94
|
+
# Add the public key to the host's authorized_keys:
|
|
95
|
+
ssh-copy-id -i ~/.ssh/id_deploy.pub deploy@1.2.3.4
|
|
96
|
+
# Then add the private key to GitHub secrets (see above)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Never commit the private key to the repository.
|
|
100
|
+
|
|
101
|
+
## General rule
|
|
102
|
+
|
|
103
|
+
Secrets in `publish.json` are always environment-variable references such as `APP_STORE_CONNECT_API_KEY_ID`.
|
|
104
|
+
Do not put literal secret values in `publish.json`, shell scripts, commit messages, or logs.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"defaults": {
|
|
4
|
+
"history_file": "project/logs/publish-history.json",
|
|
5
|
+
"mandatory_checks": ["build-ok"]
|
|
6
|
+
},
|
|
7
|
+
"targets": [
|
|
8
|
+
{
|
|
9
|
+
"name": "my-pkg",
|
|
10
|
+
"type": "npm-ci",
|
|
11
|
+
"platform": "npm-ci-oidc",
|
|
12
|
+
"checks": {},
|
|
13
|
+
"npm": { "package_name": "my-pkg", "access": "public" },
|
|
14
|
+
"github_repo": "owner/my-pkg"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"defaults": {
|
|
4
|
+
"history_file": "project/logs/publish-history.json",
|
|
5
|
+
"mandatory_checks": ["build-ok"]
|
|
6
|
+
},
|
|
7
|
+
"targets": [
|
|
8
|
+
{
|
|
9
|
+
"name": "my-pkg",
|
|
10
|
+
"type": "npm-ci",
|
|
11
|
+
"platform": "npm-ci-oidc",
|
|
12
|
+
"checks": {},
|
|
13
|
+
"secrets": {},
|
|
14
|
+
"npm": { "package_name": "my-pkg", "access": "public" },
|
|
15
|
+
"github_repo": "owner/my-pkg"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"defaults": {
|
|
4
|
+
"history_file": "project/logs/publish-history.json",
|
|
5
|
+
"mandatory_checks": ["build-ok"]
|
|
6
|
+
},
|
|
7
|
+
"targets": [
|
|
8
|
+
{
|
|
9
|
+
"name": "my-pkg",
|
|
10
|
+
"type": "npm-ci",
|
|
11
|
+
"platform": "npm-ci-oidc",
|
|
12
|
+
"checks": {},
|
|
13
|
+
"npm": { "package_name": "my-pkg", "access": "public" },
|
|
14
|
+
"github_repo": "owner/my-pkg",
|
|
15
|
+
"workflow_path": ".github/workflows/release.yml"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://jenga.local/skills/publish/schemas/publish.schema.json",
|
|
4
|
+
"title": "Publish Skill Configuration",
|
|
5
|
+
"description": "Schema for project/configs/publish.json used by the /publish skill.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"version",
|
|
10
|
+
"defaults",
|
|
11
|
+
"targets"
|
|
12
|
+
],
|
|
13
|
+
"properties": {
|
|
14
|
+
"version": {
|
|
15
|
+
"type": "integer",
|
|
16
|
+
"enum": [1]
|
|
17
|
+
},
|
|
18
|
+
"defaults": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"additionalProperties": false,
|
|
21
|
+
"required": [
|
|
22
|
+
"history_file",
|
|
23
|
+
"mandatory_checks"
|
|
24
|
+
],
|
|
25
|
+
"properties": {
|
|
26
|
+
"history_file": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"minLength": 1
|
|
29
|
+
},
|
|
30
|
+
"mandatory_checks": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"minItems": 1,
|
|
33
|
+
"uniqueItems": true,
|
|
34
|
+
"items": {
|
|
35
|
+
"$ref": "#/definitions/checkName"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"optional_checks": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"uniqueItems": true,
|
|
41
|
+
"items": {
|
|
42
|
+
"$ref": "#/definitions/checkName"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"targets": {
|
|
48
|
+
"type": "array",
|
|
49
|
+
"minItems": 1,
|
|
50
|
+
"items": {
|
|
51
|
+
"$ref": "#/definitions/target"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"definitions": {
|
|
56
|
+
"checkName": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"minLength": 1,
|
|
59
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
60
|
+
},
|
|
61
|
+
"envVarRef": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"pattern": "^\\$\\{?[A-Z][A-Z0-9_]*\\}?$"
|
|
64
|
+
},
|
|
65
|
+
"gateEntry": {
|
|
66
|
+
"oneOf": [
|
|
67
|
+
{
|
|
68
|
+
"$ref": "#/definitions/checkName"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"type": "object",
|
|
72
|
+
"additionalProperties": true,
|
|
73
|
+
"properties": {
|
|
74
|
+
"name": { "$ref": "#/definitions/checkName" },
|
|
75
|
+
"gate": { "$ref": "#/definitions/checkName" },
|
|
76
|
+
"type": { "$ref": "#/definitions/checkName" }
|
|
77
|
+
},
|
|
78
|
+
"anyOf": [
|
|
79
|
+
{ "required": ["name"] },
|
|
80
|
+
{ "required": ["gate"] },
|
|
81
|
+
{ "required": ["type"] }
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
"gateList": {
|
|
87
|
+
"type": "array",
|
|
88
|
+
"items": {
|
|
89
|
+
"$ref": "#/definitions/gateEntry"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"checksObject": {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"additionalProperties": false,
|
|
95
|
+
"properties": {
|
|
96
|
+
"pre": {
|
|
97
|
+
"$ref": "#/definitions/gateList"
|
|
98
|
+
},
|
|
99
|
+
"post": {
|
|
100
|
+
"$ref": "#/definitions/gateList"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"iosSecretMap": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"additionalProperties": false,
|
|
107
|
+
"required": [
|
|
108
|
+
"app_store_connect_api_key_id",
|
|
109
|
+
"app_store_connect_issuer_id",
|
|
110
|
+
"app_store_connect_private_key_path",
|
|
111
|
+
"code_sign_identity",
|
|
112
|
+
"provisioning_profile_uuid"
|
|
113
|
+
],
|
|
114
|
+
"properties": {
|
|
115
|
+
"app_store_connect_api_key_id": { "$ref": "#/definitions/envVarRef" },
|
|
116
|
+
"app_store_connect_issuer_id": { "$ref": "#/definitions/envVarRef" },
|
|
117
|
+
"app_store_connect_private_key_path": { "$ref": "#/definitions/envVarRef" },
|
|
118
|
+
"code_sign_identity": { "$ref": "#/definitions/envVarRef" },
|
|
119
|
+
"provisioning_profile_uuid": { "$ref": "#/definitions/envVarRef" }
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"npmSecretMap": {
|
|
123
|
+
"type": "object",
|
|
124
|
+
"additionalProperties": false,
|
|
125
|
+
"properties": {
|
|
126
|
+
"NPM_TOKEN": { "$ref": "#/definitions/envVarRef" },
|
|
127
|
+
"NODE_AUTH_TOKEN": { "$ref": "#/definitions/envVarRef" }
|
|
128
|
+
},
|
|
129
|
+
"anyOf": [
|
|
130
|
+
{ "required": ["NPM_TOKEN"] },
|
|
131
|
+
{ "required": ["NODE_AUTH_TOKEN"] }
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
"iosSettings": {
|
|
135
|
+
"type": "object",
|
|
136
|
+
"additionalProperties": false,
|
|
137
|
+
"required": [
|
|
138
|
+
"scheme",
|
|
139
|
+
"configuration",
|
|
140
|
+
"archive_path",
|
|
141
|
+
"export_path",
|
|
142
|
+
"export_method",
|
|
143
|
+
"bundle_id",
|
|
144
|
+
"team_id",
|
|
145
|
+
"app_store_app_id"
|
|
146
|
+
],
|
|
147
|
+
"properties": {
|
|
148
|
+
"project_path": {
|
|
149
|
+
"type": "string",
|
|
150
|
+
"minLength": 1
|
|
151
|
+
},
|
|
152
|
+
"workspace_path": {
|
|
153
|
+
"type": "string",
|
|
154
|
+
"minLength": 1
|
|
155
|
+
},
|
|
156
|
+
"scheme": {
|
|
157
|
+
"type": "string",
|
|
158
|
+
"minLength": 1
|
|
159
|
+
},
|
|
160
|
+
"configuration": {
|
|
161
|
+
"type": "string",
|
|
162
|
+
"minLength": 1
|
|
163
|
+
},
|
|
164
|
+
"archive_path": {
|
|
165
|
+
"type": "string",
|
|
166
|
+
"minLength": 1
|
|
167
|
+
},
|
|
168
|
+
"export_path": {
|
|
169
|
+
"type": "string",
|
|
170
|
+
"minLength": 1
|
|
171
|
+
},
|
|
172
|
+
"export_method": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"enum": ["ad-hoc", "app-store"]
|
|
175
|
+
},
|
|
176
|
+
"bundle_id": {
|
|
177
|
+
"type": "string",
|
|
178
|
+
"minLength": 1
|
|
179
|
+
},
|
|
180
|
+
"team_id": {
|
|
181
|
+
"type": "string",
|
|
182
|
+
"pattern": "^[A-Z0-9]{6,}$"
|
|
183
|
+
},
|
|
184
|
+
"app_store_app_id": {
|
|
185
|
+
"type": "string",
|
|
186
|
+
"pattern": "^[0-9]+$"
|
|
187
|
+
},
|
|
188
|
+
"provider_short_name": {
|
|
189
|
+
"type": "string",
|
|
190
|
+
"minLength": 1
|
|
191
|
+
},
|
|
192
|
+
"destination": {
|
|
193
|
+
"type": "string",
|
|
194
|
+
"minLength": 1
|
|
195
|
+
},
|
|
196
|
+
"sdk": {
|
|
197
|
+
"type": "string",
|
|
198
|
+
"minLength": 1
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"oneOf": [
|
|
202
|
+
{ "required": ["project_path"] },
|
|
203
|
+
{ "required": ["workspace_path"] }
|
|
204
|
+
]
|
|
205
|
+
},
|
|
206
|
+
"npmSettings": {
|
|
207
|
+
"description": "Settings for an npm publish target. `package_name` and `access` are required; `registry` and `dist_tag` are optional with documented defaults.",
|
|
208
|
+
"type": "object",
|
|
209
|
+
"additionalProperties": false,
|
|
210
|
+
"required": [
|
|
211
|
+
"package_name",
|
|
212
|
+
"access"
|
|
213
|
+
],
|
|
214
|
+
"properties": {
|
|
215
|
+
"package_name": {
|
|
216
|
+
"description": "The npm package name as it will appear in the registry. Supports scoped (`@scope/name`) and unscoped names. Must be lowercase, url-safe, and follow npm naming rules.",
|
|
217
|
+
"type": "string",
|
|
218
|
+
"minLength": 1,
|
|
219
|
+
"maxLength": 214,
|
|
220
|
+
"pattern": "^(?:@[a-z0-9][a-z0-9._-]*\\/)?[a-z0-9][a-z0-9._-]*$"
|
|
221
|
+
},
|
|
222
|
+
"access": {
|
|
223
|
+
"description": "Registry access level. `public` publishes an openly-installable package; `restricted` publishes a private package (paid npm/GitHub Packages).",
|
|
224
|
+
"type": "string",
|
|
225
|
+
"enum": ["public", "restricted"]
|
|
226
|
+
},
|
|
227
|
+
"registry": {
|
|
228
|
+
"description": "Registry URL to publish to. Defaults to the public npm registry when omitted by the pipeline.",
|
|
229
|
+
"type": "string",
|
|
230
|
+
"format": "uri",
|
|
231
|
+
"minLength": 1,
|
|
232
|
+
"default": "https://registry.npmjs.org"
|
|
233
|
+
},
|
|
234
|
+
"dist_tag": {
|
|
235
|
+
"description": "npm dist-tag applied to this publish (e.g. `latest`, `next`, `beta`). Defaults to `latest` when omitted by the pipeline.",
|
|
236
|
+
"type": "string",
|
|
237
|
+
"minLength": 1,
|
|
238
|
+
"pattern": "^[a-z0-9][a-z0-9._-]*$",
|
|
239
|
+
"default": "latest"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
"dropletSecretMap": {
|
|
244
|
+
"description": "Secret name references for a droplet SSH target. Values are GitHub Actions secret names, not the secret values themselves.",
|
|
245
|
+
"type": "object",
|
|
246
|
+
"additionalProperties": false,
|
|
247
|
+
"required": [
|
|
248
|
+
"ssh_key_secret",
|
|
249
|
+
"known_hosts_secret"
|
|
250
|
+
],
|
|
251
|
+
"properties": {
|
|
252
|
+
"ssh_key_secret": {
|
|
253
|
+
"description": "Name of the GitHub Actions secret that holds the SSH private key (e.g. DROPLET_SSH_KEY).",
|
|
254
|
+
"type": "string",
|
|
255
|
+
"minLength": 1
|
|
256
|
+
},
|
|
257
|
+
"known_hosts_secret": {
|
|
258
|
+
"description": "Name of the GitHub Actions secret that holds the known_hosts entry for the target host (e.g. DROPLET_KNOWN_HOSTS).",
|
|
259
|
+
"type": "string",
|
|
260
|
+
"minLength": 1
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
"dropletSettings": {
|
|
265
|
+
"description": "Settings for a droplet (or any SSH-reachable Linux host) deploy target. Deployment is orchestrated via a generated GitHub Actions workflow that SSHes into the host.",
|
|
266
|
+
"type": "object",
|
|
267
|
+
"additionalProperties": false,
|
|
268
|
+
"required": [
|
|
269
|
+
"host",
|
|
270
|
+
"ssh_user",
|
|
271
|
+
"deploy_path",
|
|
272
|
+
"github_repo",
|
|
273
|
+
"deploy_branch",
|
|
274
|
+
"start_cmd"
|
|
275
|
+
],
|
|
276
|
+
"properties": {
|
|
277
|
+
"host": {
|
|
278
|
+
"description": "IP address or FQDN of the target host.",
|
|
279
|
+
"type": "string",
|
|
280
|
+
"minLength": 1
|
|
281
|
+
},
|
|
282
|
+
"ssh_user": {
|
|
283
|
+
"description": "SSH username used to connect to the target host.",
|
|
284
|
+
"type": "string",
|
|
285
|
+
"minLength": 1
|
|
286
|
+
},
|
|
287
|
+
"ssh_port": {
|
|
288
|
+
"description": "SSH port on the target host. Defaults to 22 when omitted.",
|
|
289
|
+
"type": "integer",
|
|
290
|
+
"default": 22
|
|
291
|
+
},
|
|
292
|
+
"deploy_path": {
|
|
293
|
+
"description": "Absolute path on the remote host where the repository is checked out.",
|
|
294
|
+
"type": "string",
|
|
295
|
+
"minLength": 1
|
|
296
|
+
},
|
|
297
|
+
"github_repo": {
|
|
298
|
+
"description": "GitHub repository in owner/name format. Used to trigger the workflow via gh CLI.",
|
|
299
|
+
"type": "string",
|
|
300
|
+
"pattern": "^[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+$"
|
|
301
|
+
},
|
|
302
|
+
"deploy_branch": {
|
|
303
|
+
"description": "Git branch to pull and deploy on the remote host.",
|
|
304
|
+
"type": "string",
|
|
305
|
+
"minLength": 1
|
|
306
|
+
},
|
|
307
|
+
"build_cmd": {
|
|
308
|
+
"description": "Optional build command to run on the remote host after pulling (e.g. npm run build). Omit or leave empty to skip the build step.",
|
|
309
|
+
"type": "string",
|
|
310
|
+
"minLength": 1
|
|
311
|
+
},
|
|
312
|
+
"start_cmd": {
|
|
313
|
+
"description": "Command to (re)start the service on the remote host after deploy (e.g. pm2 reload ecosystem.config.js, sudo systemctl restart myapp).",
|
|
314
|
+
"type": "string",
|
|
315
|
+
"minLength": 1
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
"target": {
|
|
320
|
+
"type": "object",
|
|
321
|
+
"additionalProperties": false,
|
|
322
|
+
"required": [
|
|
323
|
+
"name",
|
|
324
|
+
"type",
|
|
325
|
+
"platform",
|
|
326
|
+
"checks"
|
|
327
|
+
],
|
|
328
|
+
"properties": {
|
|
329
|
+
"name": {
|
|
330
|
+
"type": "string",
|
|
331
|
+
"pattern": "^[a-z0-9][a-z0-9-]*$"
|
|
332
|
+
},
|
|
333
|
+
"type": {
|
|
334
|
+
"type": "string",
|
|
335
|
+
"enum": ["mobile-ios", "npm", "droplet", "npm-ci"]
|
|
336
|
+
},
|
|
337
|
+
"platform": {
|
|
338
|
+
"type": "string",
|
|
339
|
+
"enum": ["ios-app-store", "npm-registry", "github-packages", "droplet-ssh", "npm-ci-oidc"]
|
|
340
|
+
},
|
|
341
|
+
"checks": {
|
|
342
|
+
"$ref": "#/definitions/checksObject"
|
|
343
|
+
},
|
|
344
|
+
"secrets": {
|
|
345
|
+
"type": "object"
|
|
346
|
+
},
|
|
347
|
+
"ios": {
|
|
348
|
+
"$ref": "#/definitions/iosSettings"
|
|
349
|
+
},
|
|
350
|
+
"npm": {
|
|
351
|
+
"$ref": "#/definitions/npmSettings"
|
|
352
|
+
},
|
|
353
|
+
"droplet": {
|
|
354
|
+
"$ref": "#/definitions/dropletSettings"
|
|
355
|
+
},
|
|
356
|
+
"github_repo": {
|
|
357
|
+
"description": "GitHub repository in owner/name format. Required for npm-ci targets; used to identify the repository that hosts the trusted-publisher workflow.",
|
|
358
|
+
"type": "string",
|
|
359
|
+
"pattern": "^[^/]+/[^/]+$"
|
|
360
|
+
},
|
|
361
|
+
"workflow_path": {
|
|
362
|
+
"description": "Path within the repository to the GitHub Actions workflow file that publishes to npm. Defaults to .github/workflows/npm-publish.yml.",
|
|
363
|
+
"type": "string",
|
|
364
|
+
"minLength": 1,
|
|
365
|
+
"default": ".github/workflows/npm-publish.yml"
|
|
366
|
+
},
|
|
367
|
+
"notes": {
|
|
368
|
+
"type": "string"
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
"allOf": [
|
|
372
|
+
{
|
|
373
|
+
"if": {
|
|
374
|
+
"properties": { "type": { "const": "mobile-ios" } },
|
|
375
|
+
"required": ["type"]
|
|
376
|
+
},
|
|
377
|
+
"then": {
|
|
378
|
+
"required": ["ios", "secrets"],
|
|
379
|
+
"not": { "required": ["npm"] },
|
|
380
|
+
"properties": {
|
|
381
|
+
"platform": { "const": "ios-app-store" },
|
|
382
|
+
"secrets": { "$ref": "#/definitions/iosSecretMap" }
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"if": {
|
|
388
|
+
"properties": { "type": { "const": "npm" } },
|
|
389
|
+
"required": ["type"]
|
|
390
|
+
},
|
|
391
|
+
"then": {
|
|
392
|
+
"required": ["npm", "secrets"],
|
|
393
|
+
"not": { "required": ["ios"] },
|
|
394
|
+
"properties": {
|
|
395
|
+
"platform": { "enum": ["npm-registry", "github-packages"] },
|
|
396
|
+
"secrets": { "$ref": "#/definitions/npmSecretMap" }
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
"if": {
|
|
402
|
+
"properties": { "type": { "const": "droplet" } },
|
|
403
|
+
"required": ["type"]
|
|
404
|
+
},
|
|
405
|
+
"then": {
|
|
406
|
+
"required": ["droplet", "secrets"],
|
|
407
|
+
"properties": {
|
|
408
|
+
"platform": { "const": "droplet-ssh" },
|
|
409
|
+
"secrets": { "$ref": "#/definitions/dropletSecretMap" }
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
"if": {
|
|
415
|
+
"properties": { "type": { "const": "npm-ci" } },
|
|
416
|
+
"required": ["type"]
|
|
417
|
+
},
|
|
418
|
+
"then": {
|
|
419
|
+
"required": ["npm", "github_repo"],
|
|
420
|
+
"properties": {
|
|
421
|
+
"platform": { "const": "npm-ci-oidc" }
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
]
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -u
|
|
3
|
+
|
|
4
|
+
TARGET_NAME="${1:-}"
|
|
5
|
+
CONFIG_PATH="${2:-}"
|
|
6
|
+
EXIT_CONFIG_INVALID=4
|
|
7
|
+
|
|
8
|
+
fail() {
|
|
9
|
+
printf 'target config check failed: %s\n' "$1" >&2
|
|
10
|
+
exit "$EXIT_CONFIG_INVALID"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
[[ -n "$TARGET_NAME" ]] || fail "missing target name argument"
|
|
14
|
+
[[ -n "$CONFIG_PATH" ]] || fail "missing publish.json path argument"
|
|
15
|
+
[[ -f "$CONFIG_PATH" ]] || fail "publish.json not found at '$CONFIG_PATH'"
|
|
16
|
+
command -v jq >/dev/null 2>&1 || fail "jq is required"
|
|
17
|
+
jq -e . "$CONFIG_PATH" >/dev/null 2>&1 || fail "publish.json is not valid JSON"
|
|
18
|
+
|
|
19
|
+
TARGET_JSON="$(jq -c --arg name "$TARGET_NAME" '.targets[]? | select(.name == $name)' "$CONFIG_PATH")"
|
|
20
|
+
[[ -n "$TARGET_JSON" ]] || fail "target '$TARGET_NAME' was not found in '$CONFIG_PATH'"
|
|
21
|
+
|
|
22
|
+
TARGET_TYPE="$(printf '%s\n' "$TARGET_JSON" | jq -r '.type // empty')"
|
|
23
|
+
[[ -n "$TARGET_TYPE" ]] || fail "target '$TARGET_NAME' is missing a type"
|
|
24
|
+
|
|
25
|
+
case "$TARGET_TYPE" in
|
|
26
|
+
mobile-ios)
|
|
27
|
+
MISSING="$(printf '%s\n' "$TARGET_JSON" | jq -r '
|
|
28
|
+
def non_empty($value):
|
|
29
|
+
($value | type) == "string" and (($value | length) > 0);
|
|
30
|
+
def env_ref($value):
|
|
31
|
+
($value | type) == "string" and ($value | test("^\\$\\{?[A-Z][A-Z0-9_]*\\}?$"));
|
|
32
|
+
def missing_string($path; $value):
|
|
33
|
+
if non_empty($value // "") then empty else $path end;
|
|
34
|
+
def missing_env_ref($path; $value):
|
|
35
|
+
if env_ref($value // "") then empty else $path end;
|
|
36
|
+
[
|
|
37
|
+
(if .platform == "ios-app-store" then empty else "platform" end),
|
|
38
|
+
(if (.checks | type) == "object" then empty else "checks" end),
|
|
39
|
+
(if (.checks.pre | type) == "array" then empty else "checks.pre" end),
|
|
40
|
+
(if (.checks.post | type) == "array" then empty else "checks.post" end),
|
|
41
|
+
(missing_env_ref("secrets.app_store_connect_api_key_id"; .secrets.app_store_connect_api_key_id)),
|
|
42
|
+
(missing_env_ref("secrets.app_store_connect_issuer_id"; .secrets.app_store_connect_issuer_id)),
|
|
43
|
+
(missing_env_ref("secrets.app_store_connect_private_key_path"; .secrets.app_store_connect_private_key_path)),
|
|
44
|
+
(missing_env_ref("secrets.code_sign_identity"; .secrets.code_sign_identity)),
|
|
45
|
+
(missing_env_ref("secrets.provisioning_profile_uuid"; .secrets.provisioning_profile_uuid)),
|
|
46
|
+
(missing_string("ios.scheme"; .ios.scheme)),
|
|
47
|
+
(missing_string("ios.configuration"; .ios.configuration)),
|
|
48
|
+
(missing_string("ios.archive_path"; .ios.archive_path)),
|
|
49
|
+
(missing_string("ios.export_path"; .ios.export_path)),
|
|
50
|
+
(if (.ios.export_method == "ad-hoc" or .ios.export_method == "app-store") then empty else "ios.export_method" end),
|
|
51
|
+
(missing_string("ios.bundle_id"; .ios.bundle_id)),
|
|
52
|
+
(if ((.ios.team_id // "") | type) == "string" and ((.ios.team_id // "") | test("^[A-Z0-9]{6,}$")) then empty else "ios.team_id" end),
|
|
53
|
+
(if ((.ios.app_store_app_id // "") | type) == "string" and ((.ios.app_store_app_id // "") | test("^[0-9]+$")) then empty else "ios.app_store_app_id" end),
|
|
54
|
+
(if ((.ios.project_path // "") | length) > 0 or ((.ios.workspace_path // "") | length) > 0 then empty else "ios.project_path|ios.workspace_path" end)
|
|
55
|
+
]
|
|
56
|
+
| map(select(. != null and . != ""))
|
|
57
|
+
| .[]
|
|
58
|
+
' 2>/dev/null || true)"
|
|
59
|
+
;;
|
|
60
|
+
npm-ci)
|
|
61
|
+
MISSING="$(printf '%s\n' "$TARGET_JSON" | jq -r '
|
|
62
|
+
def non_empty($value):
|
|
63
|
+
($value | type) == "string" and (($value | length) > 0);
|
|
64
|
+
def missing_string($path; $value):
|
|
65
|
+
if non_empty($value // "") then empty else $path end;
|
|
66
|
+
[
|
|
67
|
+
(missing_string("github_repo"; .github_repo)),
|
|
68
|
+
(if (.npm | type) == "object" then empty else "npm" end),
|
|
69
|
+
(missing_string("npm.package_name"; .npm.package_name)),
|
|
70
|
+
(if ((.npm.access? // "") == "public" or (.npm.access? // "") == "restricted") then empty else "npm.access" end)
|
|
71
|
+
]
|
|
72
|
+
| map(select(. != null and . != ""))
|
|
73
|
+
| .[]
|
|
74
|
+
' 2>/dev/null || true)"
|
|
75
|
+
;;
|
|
76
|
+
*)
|
|
77
|
+
fail "unsupported target type '$TARGET_TYPE' for target '$TARGET_NAME'"
|
|
78
|
+
;;
|
|
79
|
+
esac
|
|
80
|
+
|
|
81
|
+
if [[ -n "$MISSING" ]]; then
|
|
82
|
+
MESSAGE=""
|
|
83
|
+
while IFS= read -r item; do
|
|
84
|
+
[[ -n "$item" ]] || continue
|
|
85
|
+
if [[ -z "$MESSAGE" ]]; then
|
|
86
|
+
MESSAGE="$item"
|
|
87
|
+
else
|
|
88
|
+
MESSAGE="$MESSAGE, $item"
|
|
89
|
+
fi
|
|
90
|
+
done <<EOF_MISSING
|
|
91
|
+
$MISSING
|
|
92
|
+
EOF_MISSING
|
|
93
|
+
fail "target '$TARGET_NAME' is incomplete. Missing or invalid: $MESSAGE"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
exit 0
|