@factiii/stack 0.1.23 → 0.1.25

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.
Files changed (159) hide show
  1. package/README.md +81 -60
  2. package/bin/{factiii → stack} +12 -12
  3. package/dist/cli/check-config.js +1 -1
  4. package/dist/cli/check-config.js.map +1 -1
  5. package/dist/cli/deploy-secrets.d.ts.map +1 -1
  6. package/dist/cli/deploy-secrets.js +11 -11
  7. package/dist/cli/deploy-secrets.js.map +1 -1
  8. package/dist/cli/deploy.d.ts.map +1 -1
  9. package/dist/cli/deploy.js +19 -9
  10. package/dist/cli/deploy.js.map +1 -1
  11. package/dist/cli/dev-sync.d.ts.map +1 -1
  12. package/dist/cli/dev-sync.js +10 -9
  13. package/dist/cli/dev-sync.js.map +1 -1
  14. package/dist/cli/execute-plugin-command.d.ts.map +1 -1
  15. package/dist/cli/execute-plugin-command.js +7 -7
  16. package/dist/cli/execute-plugin-command.js.map +1 -1
  17. package/dist/cli/fix.d.ts.map +1 -1
  18. package/dist/cli/fix.js +24 -4
  19. package/dist/cli/fix.js.map +1 -1
  20. package/dist/cli/init.d.ts +3 -1
  21. package/dist/cli/init.d.ts.map +1 -1
  22. package/dist/cli/init.js +114 -71
  23. package/dist/cli/init.js.map +1 -1
  24. package/dist/cli/pr-check.d.ts.map +1 -1
  25. package/dist/cli/pr-check.js +5 -4
  26. package/dist/cli/pr-check.js.map +1 -1
  27. package/dist/cli/scan.d.ts +6 -6
  28. package/dist/cli/scan.d.ts.map +1 -1
  29. package/dist/cli/scan.js +20 -19
  30. package/dist/cli/scan.js.map +1 -1
  31. package/dist/cli/secrets.d.ts.map +1 -1
  32. package/dist/cli/secrets.js +17 -16
  33. package/dist/cli/secrets.js.map +1 -1
  34. package/dist/cli/undeploy.d.ts.map +1 -1
  35. package/dist/cli/undeploy.js +4 -4
  36. package/dist/cli/undeploy.js.map +1 -1
  37. package/dist/cli/upgrade.d.ts +1 -1
  38. package/dist/cli/upgrade.js +5 -5
  39. package/dist/cli/upgrade.js.map +1 -1
  40. package/dist/cli/validate.js +1 -1
  41. package/dist/cli/validate.js.map +1 -1
  42. package/dist/constants/config-files.d.ts +17 -0
  43. package/dist/constants/config-files.d.ts.map +1 -0
  44. package/dist/constants/config-files.js +73 -0
  45. package/dist/constants/config-files.js.map +1 -0
  46. package/dist/generators/{generate-factiii-auto.d.ts → generate-stack-auto.d.ts} +4 -4
  47. package/dist/generators/generate-stack-auto.d.ts.map +1 -0
  48. package/dist/generators/{generate-factiii-auto.js → generate-stack-auto.js} +15 -13
  49. package/dist/generators/generate-stack-auto.js.map +1 -0
  50. package/dist/generators/{generate-factiii-yml.d.ts → generate-stack-yml.d.ts} +5 -5
  51. package/dist/generators/generate-stack-yml.d.ts.map +1 -0
  52. package/dist/generators/{generate-factiii-yml.js → generate-stack-yml.js} +16 -15
  53. package/dist/generators/generate-stack-yml.js.map +1 -0
  54. package/dist/generators/index.d.ts +2 -2
  55. package/dist/generators/index.d.ts.map +1 -1
  56. package/dist/generators/index.js +5 -5
  57. package/dist/generators/index.js.map +1 -1
  58. package/dist/plugins/pipelines/aws/index.d.ts.map +1 -1
  59. package/dist/plugins/pipelines/aws/index.js +17 -6
  60. package/dist/plugins/pipelines/aws/index.js.map +1 -1
  61. package/dist/plugins/pipelines/aws/policies/bootstrap-policy.json +135 -0
  62. package/dist/plugins/pipelines/aws/prod.d.ts.map +1 -1
  63. package/dist/plugins/pipelines/aws/prod.js +17 -2
  64. package/dist/plugins/pipelines/aws/prod.js.map +1 -1
  65. package/dist/plugins/pipelines/aws/scanfix/credentials.d.ts.map +1 -1
  66. package/dist/plugins/pipelines/aws/scanfix/credentials.js +40 -13
  67. package/dist/plugins/pipelines/aws/scanfix/credentials.js.map +1 -1
  68. package/dist/plugins/pipelines/aws/scanfix/ec2.d.ts.map +1 -1
  69. package/dist/plugins/pipelines/aws/scanfix/ec2.js +7 -3
  70. package/dist/plugins/pipelines/aws/scanfix/ec2.js.map +1 -1
  71. package/dist/plugins/pipelines/aws/scanfix/iam.js +2 -2
  72. package/dist/plugins/pipelines/aws/scanfix/iam.js.map +1 -1
  73. package/dist/plugins/pipelines/aws/scanfix/rds.js +2 -2
  74. package/dist/plugins/pipelines/aws/scanfix/rds.js.map +1 -1
  75. package/dist/plugins/pipelines/factiii/index.js +1 -1
  76. package/dist/plugins/pipelines/factiii/index.js.map +1 -1
  77. package/dist/plugins/pipelines/factiii/pr-check.d.ts +1 -1
  78. package/dist/plugins/pipelines/factiii/pr-check.js +1 -1
  79. package/dist/plugins/pipelines/factiii/prod.d.ts.map +1 -1
  80. package/dist/plugins/pipelines/factiii/prod.js +3 -3
  81. package/dist/plugins/pipelines/factiii/prod.js.map +1 -1
  82. package/dist/plugins/pipelines/factiii/scanfix/config.d.ts +1 -1
  83. package/dist/plugins/pipelines/factiii/scanfix/config.d.ts.map +1 -1
  84. package/dist/plugins/pipelines/factiii/scanfix/config.js +10 -11
  85. package/dist/plugins/pipelines/factiii/scanfix/config.js.map +1 -1
  86. package/dist/plugins/pipelines/factiii/scanfix/secrets.js +6 -6
  87. package/dist/plugins/pipelines/factiii/scanfix/secrets.js.map +1 -1
  88. package/dist/plugins/pipelines/factiii/scanfix/workflows.d.ts.map +1 -1
  89. package/dist/plugins/pipelines/factiii/scanfix/workflows.js +29 -25
  90. package/dist/plugins/pipelines/factiii/scanfix/workflows.js.map +1 -1
  91. package/dist/plugins/pipelines/factiii/staging.d.ts +1 -1
  92. package/dist/plugins/pipelines/factiii/staging.d.ts.map +1 -1
  93. package/dist/plugins/pipelines/factiii/staging.js +3 -2
  94. package/dist/plugins/pipelines/factiii/staging.js.map +1 -1
  95. package/dist/plugins/pipelines/factiii/utils/workflows.js +15 -15
  96. package/dist/plugins/pipelines/factiii/utils/workflows.js.map +1 -1
  97. package/dist/plugins/pipelines/factiii/workflows/factiii-cicd-prod.yml +11 -8
  98. package/dist/plugins/pipelines/factiii/workflows/factiii-cicd-staging.yml +11 -8
  99. package/dist/plugins/pipelines/factiii/workflows/factiii-command.yml +10 -8
  100. package/dist/plugins/pipelines/factiii/workflows/factiii-deploy.yml +17 -13
  101. package/dist/plugins/pipelines/factiii/workflows/factiii-dev-sync.yml +13 -11
  102. package/dist/plugins/pipelines/factiii/workflows/factiii-fix.yml +13 -11
  103. package/dist/plugins/pipelines/factiii/workflows/factiii-pr-check.yml +10 -7
  104. package/dist/plugins/pipelines/factiii/workflows/factiii-scan.yml +18 -11
  105. package/dist/plugins/pipelines/factiii/workflows/factiii-undeploy.yml +9 -8
  106. package/dist/plugins/pipelines/factiii/workflows/stack-cicd-prod.yml +115 -0
  107. package/dist/plugins/pipelines/factiii/workflows/stack-cicd-staging.yml +120 -0
  108. package/dist/plugins/pipelines/factiii/workflows/stack-command.yml +132 -0
  109. package/dist/plugins/pipelines/factiii/workflows/stack-deploy.yml +202 -0
  110. package/dist/plugins/pipelines/factiii/workflows/stack-dev-sync.yml +181 -0
  111. package/dist/plugins/pipelines/factiii/workflows/stack-fix.yml +177 -0
  112. package/dist/plugins/pipelines/factiii/workflows/stack-pr-check.yml +106 -0
  113. package/dist/plugins/pipelines/factiii/workflows/stack-scan.yml +182 -0
  114. package/dist/plugins/pipelines/factiii/workflows/stack-undeploy.yml +96 -0
  115. package/dist/plugins/servers/mac/scanfix/containers.js +1 -1
  116. package/dist/plugins/servers/mac/scanfix/containers.js.map +1 -1
  117. package/dist/scanfix/fixes/pnpm.d.ts.map +1 -1
  118. package/dist/scanfix/fixes/pnpm.js +2 -2
  119. package/dist/scanfix/fixes/pnpm.js.map +1 -1
  120. package/dist/scripts/generate-all.d.ts.map +1 -1
  121. package/dist/scripts/generate-all.js +9 -8
  122. package/dist/scripts/generate-all.js.map +1 -1
  123. package/dist/scripts/get-repo-name.d.ts +1 -1
  124. package/dist/scripts/get-repo-name.js +5 -3
  125. package/dist/scripts/get-repo-name.js.map +1 -1
  126. package/dist/scripts/validate-env-files.js +7 -1
  127. package/dist/scripts/validate-env-files.js.map +1 -1
  128. package/dist/scripts/validate-example-values.d.ts +1 -1
  129. package/dist/scripts/validate-example-values.js +3 -2
  130. package/dist/scripts/validate-example-values.js.map +1 -1
  131. package/dist/scripts/validate-stack-yml.d.ts +6 -0
  132. package/dist/scripts/validate-stack-yml.d.ts.map +1 -0
  133. package/dist/scripts/{validate-factiii-yml.js → validate-stack-yml.js} +15 -11
  134. package/dist/scripts/validate-stack-yml.js.map +1 -0
  135. package/dist/utils/config-validator.d.ts +2 -2
  136. package/dist/utils/config-validator.d.ts.map +1 -1
  137. package/dist/utils/config-validator.js +7 -6
  138. package/dist/utils/config-validator.js.map +1 -1
  139. package/dist/utils/config-writer.d.ts +16 -0
  140. package/dist/utils/config-writer.d.ts.map +1 -0
  141. package/dist/utils/config-writer.js +95 -0
  142. package/dist/utils/config-writer.js.map +1 -0
  143. package/dist/utils/deployment-report.js +4 -4
  144. package/dist/utils/deployment-report.js.map +1 -1
  145. package/dist/utils/ssh-helper.js +6 -6
  146. package/dist/utils/ssh-helper.js.map +1 -1
  147. package/dist/utils/template-generator.js +1 -1
  148. package/dist/utils/version-check.d.ts +1 -1
  149. package/dist/utils/version-check.d.ts.map +1 -1
  150. package/dist/utils/version-check.js +9 -8
  151. package/dist/utils/version-check.js.map +1 -1
  152. package/package.json +9 -3
  153. package/dist/generators/generate-factiii-auto.d.ts.map +0 -1
  154. package/dist/generators/generate-factiii-auto.js.map +0 -1
  155. package/dist/generators/generate-factiii-yml.d.ts.map +0 -1
  156. package/dist/generators/generate-factiii-yml.js.map +0 -1
  157. package/dist/scripts/validate-factiii-yml.d.ts +0 -6
  158. package/dist/scripts/validate-factiii-yml.d.ts.map +0 -1
  159. package/dist/scripts/validate-factiii-yml.js.map +0 -1
@@ -0,0 +1,202 @@
1
+ name: Stack Deploy
2
+
3
+ # Generated by @factiii/stack v{VERSION}
4
+ # INFRASTRUCTURE: Manual deployment triggered via CLI
5
+ # Run: npx stack deploy --staging or npx stack deploy --prod
6
+ # For auto-deploy on push, see stack-cicd-staging.yml / stack-cicd-prod.yml
7
+
8
+ on:
9
+ workflow_dispatch:
10
+ inputs:
11
+ environment:
12
+ description: 'Environment to deploy'
13
+ required: true
14
+ type: choice
15
+ options:
16
+ - staging
17
+ - prod
18
+
19
+ jobs:
20
+ deploy:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - name: Checkout code
24
+ uses: actions/checkout@v4
25
+
26
+ - name: Install yq
27
+ run: |
28
+ sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
29
+ sudo chmod +x /usr/local/bin/yq
30
+
31
+ - name: Read config
32
+ id: config
33
+ run: |
34
+ CONFIG_FILE="stack.yml"
35
+ if [ ! -f "$CONFIG_FILE" ]; then CONFIG_FILE="factiii.yml"; fi
36
+ if [ ! -f "$CONFIG_FILE" ]; then
37
+ echo "❌ stack.yml or factiii.yml not found"
38
+ exit 1
39
+ fi
40
+
41
+ REPO_NAME=$(yq eval '.name' "$CONFIG_FILE")
42
+
43
+ if [ "${{ inputs.environment }}" == "staging" ]; then
44
+ HOST=$(yq eval '.staging.domain // ""' "$CONFIG_FILE")
45
+ SSH_USER=$(yq eval '.staging.ssh_user // "ubuntu"' "$CONFIG_FILE")
46
+ else
47
+ HOST=$(yq eval '.prod.domain // ""' "$CONFIG_FILE")
48
+ SSH_USER=$(yq eval '.prod.ssh_user // "ubuntu"' "$CONFIG_FILE")
49
+ fi
50
+
51
+ echo "repo_name=$REPO_NAME" >> $GITHUB_OUTPUT
52
+ echo "host=$HOST" >> $GITHUB_OUTPUT
53
+ echo "ssh_user=$SSH_USER" >> $GITHUB_OUTPUT
54
+
55
+ - name: Check if environment configured
56
+ id: check_env
57
+ run: |
58
+ CONFIG_FILE="stack.yml"; if [ ! -f "$CONFIG_FILE" ]; then CONFIG_FILE="factiii.yml"; fi
59
+ if [ "${{ inputs.environment }}" == "staging" ]; then
60
+ HAS_ENV=$(yq eval '.staging != null' "$CONFIG_FILE")
61
+ else
62
+ HAS_ENV=$(yq eval '.prod != null' "$CONFIG_FILE")
63
+ fi
64
+
65
+ echo "has_env=$HAS_ENV" >> $GITHUB_OUTPUT
66
+
67
+ if [ "$HAS_ENV" != "true" ]; then
68
+ echo "⏭️ ${{ inputs.environment }} not configured in config"
69
+ exit 1
70
+ fi
71
+
72
+ - name: Read staging config (for prod builds)
73
+ if: steps.check_env.outputs.has_env == 'true' && inputs.environment == 'prod'
74
+ id: staging_config
75
+ run: |
76
+ CONFIG_FILE="stack.yml"; if [ ! -f "$CONFIG_FILE" ]; then CONFIG_FILE="factiii.yml"; fi
77
+ STAGING_HOST=$(yq eval '.staging.domain // ""' "$CONFIG_FILE")
78
+ STAGING_SSH_USER=$(yq eval '.staging.ssh_user // "ubuntu"' "$CONFIG_FILE")
79
+
80
+ echo "staging_host=$STAGING_HOST" >> $GITHUB_OUTPUT
81
+ echo "staging_ssh_user=$STAGING_SSH_USER" >> $GITHUB_OUTPUT
82
+
83
+ - name: Setup Node.js
84
+ if: steps.check_env.outputs.has_env == 'true'
85
+ uses: actions/setup-node@v4
86
+ with:
87
+ node-version: '20'
88
+
89
+ - name: Load SSH keys from Ansible Vault
90
+ if: steps.check_env.outputs.has_env == 'true'
91
+ env:
92
+ ANSIBLE_VAULT_PASSWORD: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
93
+ run: |
94
+ if [ -z "$ANSIBLE_VAULT_PASSWORD" ]; then
95
+ echo "❌ Missing ANSIBLE_VAULT_PASSWORD secret (vault password)"
96
+ exit 1
97
+ fi
98
+
99
+ npx stack secrets write-ssh-keys
100
+
101
+ # Create deploy_key symlink for current environment
102
+ if [ "${{ inputs.environment }}" == "staging" ]; then
103
+ ln -sf ~/.ssh/staging_deploy_key ~/.ssh/deploy_key
104
+ else
105
+ ln -sf ~/.ssh/prod_deploy_key ~/.ssh/deploy_key
106
+ fi
107
+
108
+ - name: Build production image on staging (prod only)
109
+ if: steps.check_env.outputs.has_env == 'true' && inputs.environment == 'prod'
110
+ env:
111
+ STAGING_HOST: ${{ steps.staging_config.outputs.staging_host }}
112
+ STAGING_USER: ${{ steps.staging_config.outputs.staging_ssh_user }}
113
+ REPO_NAME: ${{ steps.config.outputs.repo_name }}
114
+ COMMIT_HASH: ${{ github.sha }}
115
+ BRANCH: ${{ github.ref_name }}
116
+ GITHUB_REPO: ${{ github.repository }}
117
+ run: |
118
+ if [ -z "$STAGING_HOST" ]; then
119
+ echo "⚠️ Staging host not configured, skipping build step"
120
+ exit 0
121
+ fi
122
+
123
+ echo "🔨 Building production image on staging server ($STAGING_HOST)..."
124
+
125
+ ssh -i ~/.ssh/staging_deploy_key -o StrictHostKeyChecking=no "$STAGING_USER@$STAGING_HOST" \
126
+ "export PATH=\"/opt/homebrew/bin:/usr/local/bin:\$PATH\" && \
127
+ REPO_DIR=\"\$HOME/.factiii/$REPO_NAME\" && \
128
+ if [ -d \"\$REPO_DIR\" ]; then \
129
+ cd \"\$REPO_DIR\" && \
130
+ GITHUB_ACTIONS=true COMMIT_HASH=$COMMIT_HASH BRANCH=$BRANCH GITHUB_REPO=$GITHUB_REPO \
131
+ npx stack deploy --prod --commit $COMMIT_HASH --branch $BRANCH; \
132
+ else \
133
+ echo \"❌ Repo directory not found at \$REPO_DIR\"; \
134
+ exit 1; \
135
+ fi"
136
+
137
+ BUILD_EXIT_CODE=$?
138
+ if [ $BUILD_EXIT_CODE -ne 0 ]; then
139
+ echo "❌ Build step failed with exit code $BUILD_EXIT_CODE"
140
+ exit $BUILD_EXIT_CODE
141
+ fi
142
+
143
+ echo "✅ Production image built and pushed to ECR"
144
+
145
+ - name: Deploy via CLI
146
+ if: steps.check_env.outputs.has_env == 'true'
147
+ env:
148
+ HOST: ${{ steps.config.outputs.host }}
149
+ USER: ${{ steps.config.outputs.ssh_user }}
150
+ REPO_NAME: ${{ steps.config.outputs.repo_name }}
151
+ ENVIRONMENT: ${{ inputs.environment }}
152
+ COMMIT_HASH: ${{ github.sha }}
153
+ BRANCH: ${{ github.ref_name }}
154
+ GITHUB_REPO: ${{ github.repository }}
155
+ STAGING_ENVS: ${{ inputs.environment == 'staging' && secrets.STAGING_ENVS || '' }}
156
+ PROD_ENVS: ${{ inputs.environment == 'prod' && secrets.PROD_ENVS || '' }}
157
+ run: |
158
+ if [ -z "$HOST" ]; then
159
+ echo "❌ Missing domain in config: $ENVIRONMENT.domain"
160
+ exit 1
161
+ fi
162
+
163
+ echo "🚀 Deploying to $ENVIRONMENT ($HOST)..."
164
+
165
+ # For prod, skip build step (already done in previous step)
166
+ SKIP_BUILD_FLAG=""
167
+ if [ "$ENVIRONMENT" == "prod" ]; then
168
+ SKIP_BUILD_FLAG="SKIP_BUILD=true"
169
+ fi
170
+
171
+ # Prepare environment variables for SSH (base64 encode to handle special characters)
172
+ ENV_VARS_EXPORT=""
173
+ if [ "$ENVIRONMENT" == "staging" ] && [ -n "$STAGING_ENVS" ]; then
174
+ ENV_VARS_B64=$(echo -n "$STAGING_ENVS" | base64 -w 0)
175
+ ENV_VARS_EXPORT="STAGING_ENVS=\$(echo '$ENV_VARS_B64' | base64 -d) && export STAGING_ENVS && "
176
+ elif [ "$ENVIRONMENT" == "prod" ] && [ -n "$PROD_ENVS" ]; then
177
+ ENV_VARS_B64=$(echo -n "$PROD_ENVS" | base64 -w 0)
178
+ ENV_VARS_EXPORT="PROD_ENVS=\$(echo '$ENV_VARS_B64' | base64 -d) && export PROD_ENVS && "
179
+ fi
180
+
181
+ ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no "$USER@$HOST" \
182
+ "export PATH=\"/opt/homebrew/bin:/usr/local/bin:\$PATH\" && \
183
+ REPO_DIR=\"\$HOME/.factiii/$REPO_NAME\" && \
184
+ if [ -d \"\$REPO_DIR\" ]; then \
185
+ cd \"\$REPO_DIR\" && \
186
+ $ENV_VARS_EXPORT$SKIP_BUILD_FLAG GITHUB_ACTIONS=true COMMIT_HASH=$COMMIT_HASH BRANCH=$BRANCH GITHUB_REPO=$GITHUB_REPO \
187
+ npx stack deploy --$ENVIRONMENT --commit $COMMIT_HASH --branch $BRANCH; \
188
+ else \
189
+ echo \"❌ Repo directory not found at \$REPO_DIR\"; \
190
+ echo \"Run deployment first to clone the repository\"; \
191
+ exit 1; \
192
+ fi"
193
+
194
+ DEPLOY_EXIT_CODE=$?
195
+ rm -f ~/.ssh/deploy_key ~/.ssh/staging_deploy_key ~/.ssh/prod_deploy_key
196
+
197
+ if [ $DEPLOY_EXIT_CODE -eq 0 ]; then
198
+ echo "✅ Deployment complete!"
199
+ else
200
+ echo "❌ Deployment failed with exit code $DEPLOY_EXIT_CODE"
201
+ exit $DEPLOY_EXIT_CODE
202
+ fi
@@ -0,0 +1,181 @@
1
+ name: Stack Dev Sync
2
+
3
+ # Generated by @factiii/stack v{VERSION}
4
+ # DEV/TESTING ONLY - Deploys locally built infrastructure for testing beta features
5
+ # This workflow receives infrastructure changes and deploys them to servers
6
+ # Use: npx stack dev-sync
7
+ #
8
+ # ⚠️ WARNING: This is for developing @factiii/stack itself, not for app deployments
9
+ # Only use this when testing new infrastructure features before releasing them
10
+
11
+ on:
12
+ workflow_dispatch:
13
+ inputs:
14
+ environment:
15
+ description: 'Environment to sync'
16
+ required: true
17
+ type: choice
18
+ options:
19
+ - staging
20
+ - prod
21
+ release_id:
22
+ description: 'GitHub Release ID containing artifact'
23
+ required: true
24
+ type: string
25
+ asset_id:
26
+ description: 'Release Asset ID for infrastructure tarball'
27
+ required: true
28
+ type: string
29
+ deploy:
30
+ description: 'Deploy after syncing'
31
+ required: false
32
+ type: boolean
33
+ default: false
34
+
35
+ jobs:
36
+ dev-sync:
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - name: Checkout code
40
+ uses: actions/checkout@v4
41
+
42
+ - name: Install yq
43
+ run: |
44
+ sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
45
+ sudo chmod +x /usr/local/bin/yq
46
+
47
+ - name: Read config
48
+ id: config
49
+ run: |
50
+ CONFIG_FILE="stack.yml"; if [ ! -f "$CONFIG_FILE" ]; then CONFIG_FILE="factiii.yml"; fi
51
+ if [ ! -f "$CONFIG_FILE" ]; then
52
+ echo "❌ stack.yml or factiii.yml not found"
53
+ exit 1
54
+ fi
55
+
56
+ REPO_NAME=$(yq eval '.name' "$CONFIG_FILE")
57
+
58
+ if [ "${{ inputs.environment }}" == "staging" ]; then
59
+ HOST=$(yq eval '.staging.domain // ""' "$CONFIG_FILE")
60
+ SSH_USER=$(yq eval '.staging.ssh_user // "ubuntu"' "$CONFIG_FILE")
61
+ else
62
+ HOST=$(yq eval '.prod.domain // ""' "$CONFIG_FILE")
63
+ SSH_USER=$(yq eval '.prod.ssh_user // "ubuntu"' "$CONFIG_FILE")
64
+ fi
65
+
66
+ echo "repo_name=$REPO_NAME" >> $GITHUB_OUTPUT
67
+ echo "host=$HOST" >> $GITHUB_OUTPUT
68
+ echo "ssh_user=$SSH_USER" >> $GITHUB_OUTPUT
69
+
70
+ - name: Check if environment configured
71
+ id: check_env
72
+ run: |
73
+ CONFIG_FILE="stack.yml"; if [ ! -f "$CONFIG_FILE" ]; then CONFIG_FILE="factiii.yml"; fi
74
+ if [ "${{ inputs.environment }}" == "staging" ]; then
75
+ HAS_ENV=$(yq eval '.staging != null' "$CONFIG_FILE")
76
+ else
77
+ HAS_ENV=$(yq eval '.prod != null' "$CONFIG_FILE")
78
+ fi
79
+
80
+ echo "has_env=$HAS_ENV" >> $GITHUB_OUTPUT
81
+
82
+ if [ "$HAS_ENV" != "true" ]; then
83
+ echo "⏭️ ${{ inputs.environment }} not configured in config"
84
+ exit 1
85
+ fi
86
+
87
+ - name: Setup SSH
88
+ if: steps.check_env.outputs.has_env == 'true'
89
+ env:
90
+ SSH_KEY: ${{ inputs.environment == 'staging' && secrets.STAGING_SSH || secrets.PROD_SSH }}
91
+ run: |
92
+ if [ -z "$SSH_KEY" ]; then
93
+ echo "❌ Missing ${{ inputs.environment == 'staging' && 'STAGING_SSH' || 'PROD_SSH' }} secret"
94
+ exit 1
95
+ fi
96
+
97
+ mkdir -p ~/.ssh
98
+ echo "$SSH_KEY" > ~/.ssh/deploy_key
99
+ chmod 600 ~/.ssh/deploy_key
100
+
101
+ - name: Download infrastructure artifact from release
102
+ if: steps.check_env.outputs.has_env == 'true'
103
+ env:
104
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
105
+ RELEASE_ID: ${{ inputs.release_id }}
106
+ ASSET_ID: ${{ inputs.asset_id }}
107
+ run: |
108
+ echo "⚠️ DEV SYNC MODE - Using infrastructure from local build"
109
+ echo " This syncs uncommitted infrastructure changes for testing"
110
+ echo " Release ID: $RELEASE_ID"
111
+ echo " Asset ID: $ASSET_ID"
112
+
113
+ echo "📦 Downloading infrastructure artifact..."
114
+
115
+ # Download release asset using GitHub API
116
+ curl -L \
117
+ -H "Accept: application/octet-stream" \
118
+ -H "Authorization: Bearer $GITHUB_TOKEN" \
119
+ -H "X-GitHub-Api-Version: 2022-11-28" \
120
+ "https://api.github.com/repos/${{ github.repository }}/releases/assets/$ASSET_ID" \
121
+ -o infrastructure.tar.gz
122
+
123
+ # Verify download
124
+ if [ ! -f infrastructure.tar.gz ]; then
125
+ echo "❌ Failed to download artifact"
126
+ exit 1
127
+ fi
128
+
129
+ SIZE=$(du -h infrastructure.tar.gz | cut -f1)
130
+ echo "✅ Downloaded artifact ($SIZE)"
131
+
132
+ - name: Deploy infrastructure to server
133
+ if: steps.check_env.outputs.has_env == 'true'
134
+ env:
135
+ HOST: ${{ steps.config.outputs.host }}
136
+ USER: ${{ steps.config.outputs.ssh_user }}
137
+ DEPLOY: ${{ inputs.deploy }}
138
+ REPO_NAME: ${{ steps.config.outputs.repo_name }}
139
+ ENVIRONMENT: ${{ inputs.environment }}
140
+ run: |
141
+ if [ -z "$HOST" ]; then
142
+ echo "❌ Missing domain in config: $ENVIRONMENT.domain"
143
+ exit 1
144
+ fi
145
+
146
+ echo "🚀 Syncing infrastructure to $ENVIRONMENT ($HOST)..."
147
+
148
+ # Copy infrastructure to server
149
+ echo "📤 Uploading infrastructure package..."
150
+ scp -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no infrastructure.tar.gz "$USER@$HOST:/tmp/"
151
+
152
+ # Extract and setup on server
153
+ echo "📦 Extracting infrastructure on server..."
154
+ ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no "$USER@$HOST" \
155
+ "export PATH=\"/opt/homebrew/bin:/usr/local/bin:\$PATH\" && \
156
+ echo \"📦 Setting up infrastructure...\" && \
157
+ mkdir -p ~/.factiii/infrastructure && \
158
+ cd ~/.factiii/infrastructure && \
159
+ tar -xzf /tmp/infrastructure.tar.gz && \
160
+ rm /tmp/infrastructure.tar.gz && \
161
+ echo \"\" && \
162
+ echo \"📋 Verifying version...\" && \
163
+ VERSION=\$(cat package.json | grep '\"version\"' | head -1 | sed 's/.*: \"\(.*\)\".*/\1/') && \
164
+ echo \" Version: \$VERSION\" && \
165
+ echo \"\" && \
166
+ echo \"✅ Infrastructure synced successfully\""
167
+
168
+ # Optionally deploy
169
+ if [ "$DEPLOY" == "true" ]; then
170
+ echo ""
171
+ echo "🚀 Deploying to $ENVIRONMENT..."
172
+ ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no "$USER@$HOST" \
173
+ "export PATH=\"/opt/homebrew/bin:/usr/local/bin:\$PATH\" && \
174
+ cd ~/.factiii/$REPO_NAME && \
175
+ GITHUB_ACTIONS=true node ~/.factiii/infrastructure/bin/stack deploy --$ENVIRONMENT"
176
+ fi
177
+
178
+ rm -f ~/.ssh/deploy_key
179
+ echo ""
180
+ echo "✅ Dev sync complete!"
181
+
@@ -0,0 +1,177 @@
1
+ name: Stack Fix
2
+
3
+ # Generated by @factiii/stack v{VERSION}
4
+ # INFRASTRUCTURE: Fix server issues via CLI
5
+ # Run: npx stack fix (triggers this workflow)
6
+ # Runs on configured environments in parallel using matrix strategy
7
+
8
+ on:
9
+ workflow_dispatch:
10
+ inputs:
11
+ environment:
12
+ description: 'Environment to fix'
13
+ required: true
14
+ type: choice
15
+ options:
16
+ - all
17
+ - staging
18
+ - prod
19
+ default: all
20
+
21
+ jobs:
22
+ setup:
23
+ runs-on: ubuntu-latest
24
+ outputs:
25
+ matrix: ${{ steps.set-matrix.outputs.matrix }}
26
+ steps:
27
+ - name: Checkout code
28
+ uses: actions/checkout@v4
29
+
30
+ - name: Install yq
31
+ run: |
32
+ sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
33
+ sudo chmod +x /usr/local/bin/yq
34
+
35
+ - name: Determine environments
36
+ id: set-matrix
37
+ run: |
38
+ ENVS="[]"
39
+
40
+ CONFIG_FILE="stack.yml"; if [ ! -f "$CONFIG_FILE" ]; then CONFIG_FILE="factiii.yml"; fi
41
+ if [ "${{ inputs.environment }}" == "all" ]; then
42
+ HAS_STAGING=$(yq eval '.staging != null' "$CONFIG_FILE")
43
+ HAS_PROD=$(yq eval '.prod != null' "$CONFIG_FILE")
44
+
45
+ if [ "$HAS_STAGING" == "true" ] && [ "$HAS_PROD" == "true" ]; then
46
+ ENVS='["staging", "prod"]'
47
+ elif [ "$HAS_STAGING" == "true" ]; then
48
+ ENVS='["staging"]'
49
+ elif [ "$HAS_PROD" == "true" ]; then
50
+ ENVS='["prod"]'
51
+ fi
52
+ else
53
+ ENVS='["${{ inputs.environment }}"]'
54
+ fi
55
+
56
+ echo "matrix={\"environment\":$ENVS}" >> $GITHUB_OUTPUT
57
+
58
+ fix:
59
+ needs: setup
60
+ runs-on: ubuntu-latest
61
+ strategy:
62
+ fail-fast: false
63
+ matrix: ${{ fromJson(needs.setup.outputs.matrix) }}
64
+ steps:
65
+ - name: Checkout code
66
+ uses: actions/checkout@v4
67
+
68
+ - name: Install yq
69
+ run: |
70
+ sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
71
+ sudo chmod +x /usr/local/bin/yq
72
+
73
+ - name: Read config
74
+ id: config
75
+ env:
76
+ ENVIRONMENT: ${{ matrix.environment }}
77
+ run: |
78
+ CONFIG_FILE="stack.yml"; if [ ! -f "$CONFIG_FILE" ]; then CONFIG_FILE="factiii.yml"; fi
79
+ if [ ! -f "$CONFIG_FILE" ]; then
80
+ echo "❌ stack.yml or factiii.yml not found"
81
+ exit 1
82
+ fi
83
+
84
+ REPO_NAME=$(yq eval '.name' "$CONFIG_FILE")
85
+ HOST=$(yq eval ".$ENVIRONMENT.domain // \"\"" "$CONFIG_FILE")
86
+ SSH_USER=$(yq eval ".$ENVIRONMENT.ssh_user // \"ubuntu\"" "$CONFIG_FILE")
87
+
88
+ echo "repo_name=$REPO_NAME" >> $GITHUB_OUTPUT
89
+ echo "host=$HOST" >> $GITHUB_OUTPUT
90
+ echo "ssh_user=$SSH_USER" >> $GITHUB_OUTPUT
91
+
92
+ - name: Check if environment configured
93
+ id: check_env
94
+ env:
95
+ ENVIRONMENT: ${{ matrix.environment }}
96
+ run: |
97
+ CONFIG_FILE="stack.yml"; if [ ! -f "$CONFIG_FILE" ]; then CONFIG_FILE="factiii.yml"; fi
98
+ HAS_ENV=$(yq eval ".$ENVIRONMENT != null" "$CONFIG_FILE")
99
+ echo "has_env=$HAS_ENV" >> $GITHUB_OUTPUT
100
+
101
+ if [ "$HAS_ENV" != "true" ]; then
102
+ echo "⏭️ $ENVIRONMENT not configured"
103
+ exit 0
104
+ fi
105
+
106
+ - name: Check SSH secret
107
+ if: steps.check_env.outputs.has_env == 'true'
108
+ env:
109
+ ENVIRONMENT: ${{ matrix.environment }}
110
+ SSH_KEY: ${{ matrix.environment == 'staging' && secrets.STAGING_SSH || secrets.PROD_SSH }}
111
+ run: |
112
+ if [ -z "$SSH_KEY" ]; then
113
+ SECRET_NAME="${ENVIRONMENT^^}_SSH"
114
+ echo "❌ ${SECRET_NAME} secret not found"
115
+ echo "Add it at: https://github.com/${{ github.repository }}/settings/secrets/actions"
116
+ exit 1
117
+ fi
118
+ echo "✅ SSH secret exists for $ENVIRONMENT"
119
+
120
+ - name: Setup SSH
121
+ if: steps.check_env.outputs.has_env == 'true'
122
+ env:
123
+ SSH_KEY: ${{ matrix.environment == 'staging' && secrets.STAGING_SSH || secrets.PROD_SSH }}
124
+ run: |
125
+ mkdir -p ~/.ssh
126
+ echo "$SSH_KEY" > ~/.ssh/deploy_key
127
+ chmod 600 ~/.ssh/deploy_key
128
+
129
+ - name: Bootstrap Node.js (one-time)
130
+ if: steps.check_env.outputs.has_env == 'true'
131
+ env:
132
+ HOST: ${{ steps.config.outputs.host }}
133
+ USER: ${{ steps.config.outputs.ssh_user }}
134
+ run: |
135
+ echo "🔍 Checking Node.js on server..."
136
+ ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no "$USER@$HOST" \
137
+ 'if ! command -v node &> /dev/null; then
138
+ echo "📦 Installing Node.js...";
139
+ if [ -f /opt/homebrew/bin/brew ] || [ -f /usr/local/bin/brew ]; then
140
+ export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH";
141
+ brew install node;
142
+ else
143
+ curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - && sudo apt-get install -y nodejs;
144
+ fi;
145
+ else
146
+ echo "✅ Node.js already installed";
147
+ fi'
148
+
149
+ - name: Fix via SSH
150
+ if: steps.check_env.outputs.has_env == 'true'
151
+ env:
152
+ HOST: ${{ steps.config.outputs.host }}
153
+ USER: ${{ steps.config.outputs.ssh_user }}
154
+ REPO_NAME: ${{ steps.config.outputs.repo_name }}
155
+ ENVIRONMENT: ${{ matrix.environment }}
156
+ run: |
157
+ if [ -z "$HOST" ]; then
158
+ echo "❌ Missing domain in config: $ENVIRONMENT.domain"
159
+ exit 1
160
+ fi
161
+
162
+ echo "🔧 Fixing $ENVIRONMENT ($HOST)..."
163
+
164
+ ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ServerAliveInterval=60 -o ServerAliveCountMax=5 "$USER@$HOST" \
165
+ "export PATH=\"/opt/homebrew/bin:/usr/local/bin:\$PATH\" && \
166
+ REPO_DIR=\"\$HOME/.factiii/$REPO_NAME\" && \
167
+ if [ -d \"\$REPO_DIR\" ]; then \
168
+ cd \"\$REPO_DIR\" && \
169
+ GITHUB_ACTIONS=true npx stack fix --$ENVIRONMENT; \
170
+ else \
171
+ echo \"❌ Repo directory not found at \$REPO_DIR\"; \
172
+ echo \"Run deployment first to clone the repository\"; \
173
+ exit 1; \
174
+ fi"
175
+
176
+ rm -f ~/.ssh/deploy_key
177
+ echo "✅ $ENVIRONMENT fix complete!"
@@ -0,0 +1,106 @@
1
+ name: Stack PR Check
2
+
3
+ # Generated by @factiii/stack v{VERSION}
4
+ # Validates server/client/mobile builds when PR opens to main.
5
+ # SSH to staging, run builds, report status to GitHub.
6
+
7
+ on:
8
+ pull_request:
9
+ branches:
10
+ - main
11
+
12
+ jobs:
13
+ pr-check:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Checkout code
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Install yq
20
+ run: |
21
+ sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
22
+ sudo chmod +x /usr/local/bin/yq
23
+
24
+ - name: Read config
25
+ id: config
26
+ run: |
27
+ CONFIG_FILE="stack.yml"
28
+ if [ ! -f "$CONFIG_FILE" ]; then CONFIG_FILE="factiii.yml"; fi
29
+ if [ ! -f "$CONFIG_FILE" ]; then
30
+ echo "❌ stack.yml or factiii.yml not found"
31
+ exit 1
32
+ fi
33
+
34
+ REPO_NAME=$(yq eval '.name' "$CONFIG_FILE")
35
+ HOST=$(yq eval '.staging.domain // ""' "$CONFIG_FILE")
36
+ SSH_USER=$(yq eval '.staging.ssh_user // "ubuntu"' "$CONFIG_FILE")
37
+
38
+ echo "repo_name=$REPO_NAME" >> $GITHUB_OUTPUT
39
+ echo "host=$HOST" >> $GITHUB_OUTPUT
40
+ echo "ssh_user=$SSH_USER" >> $GITHUB_OUTPUT
41
+
42
+ - name: Check if staging configured
43
+ id: check_staging
44
+ run: |
45
+ CONFIG_FILE="stack.yml"; if [ ! -f "$CONFIG_FILE" ]; then CONFIG_FILE="factiii.yml"; fi
46
+ HAS_STAGING=$(yq eval '.staging != null' "$CONFIG_FILE")
47
+ echo "has_staging=$HAS_STAGING" >> $GITHUB_OUTPUT
48
+
49
+ if [ "$HAS_STAGING" != "true" ]; then
50
+ echo "⏭️ Staging not configured - skipping PR check"
51
+ exit 0
52
+ fi
53
+
54
+ - name: Setup SSH
55
+ if: steps.check_staging.outputs.has_staging == 'true'
56
+ env:
57
+ SSH_KEY: ${{ secrets.STAGING_SSH }}
58
+ run: |
59
+ if [ -z "$SSH_KEY" ]; then
60
+ echo "❌ Missing STAGING_SSH secret"
61
+ exit 1
62
+ fi
63
+
64
+ mkdir -p ~/.ssh
65
+ echo "$SSH_KEY" > ~/.ssh/deploy_key
66
+ chmod 600 ~/.ssh/deploy_key
67
+
68
+ - name: Run PR check on staging
69
+ if: steps.check_staging.outputs.has_staging == 'true'
70
+ env:
71
+ HOST: ${{ steps.config.outputs.host }}
72
+ USER: ${{ steps.config.outputs.ssh_user }}
73
+ REPO_NAME: ${{ steps.config.outputs.repo_name }}
74
+ COMMIT_HASH: ${{ github.event.pull_request.head.sha }}
75
+ BRANCH: ${{ github.event.pull_request.head.ref }}
76
+ PR_NUMBER: ${{ github.event.pull_request.number }}
77
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78
+ run: |
79
+ if [ -z "$HOST" ]; then
80
+ echo "❌ Missing staging.domain in config"
81
+ exit 1
82
+ fi
83
+
84
+ echo "🔍 Running PR check on staging ($HOST)..."
85
+
86
+ GITHUB_TOKEN_B64=$(echo -n "$GITHUB_TOKEN" | base64 -w 0)
87
+ ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no "$USER@$HOST" \
88
+ "export PATH=\"/opt/homebrew/bin:/usr/local/bin:\$PATH\" && \
89
+ export GITHUB_TOKEN=\$(echo \"$GITHUB_TOKEN_B64\" | base64 -d) && \
90
+ export GITHUB_ACTIONS=true COMMIT_HASH=$COMMIT_HASH BRANCH=$BRANCH PR_NUMBER=$PR_NUMBER && \
91
+ REPO_DIR=\"\$HOME/.factiii/$REPO_NAME\" && \
92
+ if [ -d \"\$REPO_DIR\" ]; then \
93
+ cd \"\$REPO_DIR\" && npx stack pr-check --staging; \
94
+ else \
95
+ echo \"❌ Repo not found at \$REPO_DIR\"; exit 1; \
96
+ fi"
97
+
98
+ EXIT_CODE=$?
99
+ rm -f ~/.ssh/deploy_key
100
+
101
+ if [ $EXIT_CODE -eq 0 ]; then
102
+ echo "✅ PR check passed"
103
+ else
104
+ echo "❌ PR check failed"
105
+ exit $EXIT_CODE
106
+ fi