@codigodoleo/wp-kit 3.1.0 → 3.2.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.
Files changed (70) hide show
  1. package/lib/commands/init.js +19 -10
  2. package/lib/config/versions.js +5 -5
  3. package/lib/prompts/index.js +24 -14
  4. package/lib/utils/git.js +50 -13
  5. package/modules/deploy/.github/workflows/deploy-docker.yml +102 -0
  6. package/modules/deploy/.github/workflows/deploy-ssh.yml +126 -0
  7. package/modules/deploy/.github/workflows/release.yml +55 -0
  8. package/modules/deploy/index.js +39 -0
  9. package/modules/deploy/prompts.js +1 -3
  10. package/modules/deploy/templates/.gitlab/ci/deploy-docker.yml.hbs +35 -0
  11. package/modules/deploy/templates/.gitlab/ci/deploy-ssh.yml.hbs +83 -0
  12. package/modules/deploy/templates/.gitlab/ci/release.yml.hbs +26 -0
  13. package/modules/git/.husky/commit-msg +8 -4
  14. package/modules/git/.husky/pre-commit +0 -3
  15. package/modules/git/index.js +34 -38
  16. package/modules/git/templates/.lando.yml.hbs +13 -5
  17. package/modules/git/templates/package.json.hbs +4 -4
  18. package/modules/git/templates/workspace.json.hbs +114 -28
  19. package/modules/lint/templates/.lando.yml.hbs +10 -2
  20. package/modules/lint/templates/package.json.hbs +4 -4
  21. package/modules/lint/templates/workspace.json.hbs +56 -15
  22. package/modules/php/templates/.lando.yml.hbs +11 -2
  23. package/modules/php/templates/composer.json.hbs +1 -1
  24. package/modules/php/templates/workspace.json.hbs +74 -15
  25. package/modules/redis/templates/.lando.yml.hbs +8 -1
  26. package/modules/sage/index.js +114 -15
  27. package/modules/sage/prompts.js +1 -1
  28. package/modules/sage/templates/.devcontainer/devcontainer.json.hbs +7 -0
  29. package/modules/sage/templates/.devcontainer/docker-compose.override.yml.hbs +6 -0
  30. package/modules/sage/templates/.devcontainer/post-create.sh.hbs +11 -0
  31. package/modules/sage/templates/.lando.yml.hbs +49 -20
  32. package/modules/sage/templates/server/cmd/setup-sage-node.sh.hbs +23 -0
  33. package/modules/sage/templates/server/cmd/setup-sage-php.sh.hbs +24 -0
  34. package/modules/sage/templates/theme/app/Blocks/.gitkeep.hbs +0 -0
  35. package/modules/sage/templates/theme/app/Fields/.gitkeep.hbs +0 -0
  36. package/modules/sage/templates/theme/composer.json.hbs +25 -3
  37. package/modules/sage/templates/theme/package.json.hbs +15 -3
  38. package/modules/sage/templates/theme/resources/views/blocks/.gitkeep.hbs +0 -0
  39. package/modules/sage/templates/theme/vite.config.js.hbs +55 -13
  40. package/modules/sage/templates/workspace.json.hbs +155 -12
  41. package/modules/skills/index.js +18 -0
  42. package/modules/skills/prompts.js +21 -0
  43. package/modules/test/index.js +19 -0
  44. package/modules/test/prompts.js +8 -0
  45. package/modules/test/templates/composer.json.hbs +16 -0
  46. package/modules/test/templates/package.json.hbs +11 -0
  47. package/modules/test/templates/playwright.config.js.hbs +29 -0
  48. package/modules/test/tests/Pest.php +39 -0
  49. package/modules/test/tests/SmokeTest.php +15 -0
  50. package/modules/test/tests/e2e/example.spec.js +11 -0
  51. package/modules/test/tests/e2e/helpers/loginHelper.js +40 -0
  52. package/package.json +1 -1
  53. package/templates/.devcontainer/Dockerfile.hbs +24 -0
  54. package/templates/.devcontainer/config/nginx.conf +23 -0
  55. package/templates/.devcontainer/config/nginx.conf.hbs +23 -0
  56. package/templates/.devcontainer/devcontainer.json.hbs +29 -0
  57. package/templates/.devcontainer/docker-compose.yml.hbs +73 -0
  58. package/templates/.devcontainer/setup.sh.hbs +17 -0
  59. package/templates/.env.hbs +38 -15
  60. package/templates/.gitignore.hbs +2 -2
  61. package/templates/.lando.yml.hbs +88 -11
  62. package/templates/README.md.hbs +23 -7
  63. package/templates/composer.json.hbs +4 -3
  64. package/templates/package.json.hbs +0 -2
  65. package/templates/server/cmd/install-wp.sh.hbs +54 -43
  66. package/templates/server/cmd/setup-node.sh.hbs +11 -0
  67. package/templates/server/cmd/setup-php.sh.hbs +14 -0
  68. package/templates/server/www/vhosts.conf.hbs +54 -17
  69. package/templates/workspace.json.hbs +233 -40
  70. package/templates/wp-cli.yml.hbs +1 -0
@@ -9,7 +9,6 @@ import { Generator } from '../core/generator.js';
9
9
  import { log, color } from '../utils/logger.js';
10
10
  import { inferCiCapabilities } from '../core/infer-ci-capabilities.js';
11
11
  import { validateVersionCompatibility } from '../config/versions.js';
12
- import { installSuperpowersSage } from '../utils/git.js';
13
12
 
14
13
  const __filename = fileURLToPath(import.meta.url);
15
14
  const __dirname = path.dirname(__filename);
@@ -20,12 +19,11 @@ export const initCommand = new Command('init')
20
19
  .option('--debug', 'Ativa modo debug para logs detalhados')
21
20
  .action(async ({ output, defaults, debug }) => {
22
21
  const target = path.resolve(output);
23
- const { enabledModules, ...context } = await runPrompts({ defaults });
22
+ const { enabledModules: promptedModules, ...context } = await runPrompts({ defaults });
23
+ let enabledModules = [...promptedModules];
24
24
 
25
25
  // Normalização de chaves para consistência entre módulos
26
- if (context.enable_sage === true) {
27
- context.useSage = true;
28
- }
26
+ context.useSage = context.enable_sage === true;
29
27
  if (context.git_provider && !context.gitProvider) {
30
28
  context.gitProvider = context.git_provider;
31
29
  }
@@ -34,12 +32,12 @@ export const initCommand = new Command('init')
34
32
  if (context.useSage && context.sageVersion === '11') {
35
33
  context.phpVersion = '8.3';
36
34
  context.nodeVersion = '22';
37
- context.wpCoreVersion = '6.8.1';
35
+ context.wpCoreVersion = '6.9.4';
38
36
 
39
37
  log(color.orange, '🔧 Sage 11 selected — automatically adjusting versions:');
40
38
  log(color.orange, ' → PHP: 8.3');
41
39
  log(color.orange, ' → Node: 22');
42
- log(color.orange, ' → WP Core: 6.8.1');
40
+ log(color.orange, ' → WP Core: 6.9.4');
43
41
  }
44
42
 
45
43
  // 📋 Validar compatibilidade de versões (se Sage está habilitado)
@@ -71,6 +69,9 @@ export const initCommand = new Command('init')
71
69
  if (context.ci.theme.paths.themeRoot) {
72
70
  log(color.blue, ` → themeRoot: ${context.ci.theme.paths.themeRoot}`);
73
71
  }
72
+ if (context.useSage) {
73
+ log(color.blue, ' → sageMode: advanced (automatic when Sage is enabled)');
74
+ }
74
75
 
75
76
  const generator = new Generator(target);
76
77
  await fs.ensureDir(target);
@@ -80,6 +81,13 @@ export const initCommand = new Command('init')
80
81
  await generator.generateFile('.env', context);
81
82
  await generator.generateFile('.editorconfig', context);
82
83
  await generator.generateFile('server/cmd/install-wp.sh', context);
84
+ await generator.generateFile('server/cmd/setup-php.sh', context);
85
+ await generator.generateFile('server/cmd/setup-node.sh', context);
86
+ await generator.generateFile('.devcontainer/devcontainer.json', context);
87
+ await generator.generateFile('.devcontainer/docker-compose.yml', context);
88
+ await generator.generateFile('.devcontainer/Dockerfile', context);
89
+ await generator.generateFile('.devcontainer/setup.sh', context);
90
+ await generator.generateFile('.devcontainer/config/nginx.conf', context);
83
91
 
84
92
  await generator.copyFile('.cspell.json');
85
93
 
@@ -93,6 +101,9 @@ export const initCommand = new Command('init')
93
101
 
94
102
  await fs.chmod(path.join(target, 'content/uploads'), 0o777);
95
103
  await fs.chmod(path.join(target, 'server/cmd/install-wp.sh'), 0o755);
104
+ await fs.chmod(path.join(target, 'server/cmd/setup-php.sh'), 0o755);
105
+ await fs.chmod(path.join(target, 'server/cmd/setup-node.sh'), 0o755);
106
+ await fs.chmod(path.join(target, '.devcontainer/setup.sh'), 0o755);
96
107
 
97
108
  await generator.generateFile('server/www/vhosts.conf', context);
98
109
  if (context.useRocket) {
@@ -101,6 +112,7 @@ export const initCommand = new Command('init')
101
112
 
102
113
  await generator.copyFile('wp-config.php');
103
114
  await generator.copyFile('index.php');
115
+ await generator.generateFile('wp-cli.yml', context);
104
116
 
105
117
  log(color.blue, `🧩 Enabled modules: ${enabledModules.join(', ')}`);
106
118
 
@@ -154,8 +166,5 @@ export const initCommand = new Command('init')
154
166
  // Gera CLAUDE.md com overview do projeto e referência ao AI Co-Pilot
155
167
  await generator.generateFile('CLAUDE.md', context);
156
168
 
157
- // Instala superpowers-sage em .claude/ do projeto gerado
158
- await installSuperpowersSage(target);
159
-
160
169
  log(color.green, '✅ Project initialized with templates.');
161
170
  });
@@ -53,8 +53,8 @@ export const VERSION_REGISTRY = {
53
53
  * Only actively supported versions per php.net
54
54
  */
55
55
  php: {
56
- latest: '8.3',
57
- supported: ['8.3', '8.2', '8.1'],
56
+ latest: '8.4',
57
+ supported: ['8.4', '8.3', '8.2', '8.1'],
58
58
  eol: ['8.0', '7.4', '7.3', '7.2'], // Deprecated - do not use
59
59
  description: 'Supported PHP versions. PHP 7.4+ reached EOL on 2022-11-28',
60
60
  // PHP version dates: https://www.php.net/supported-versions.php
@@ -70,9 +70,9 @@ export const VERSION_REGISTRY = {
70
70
  * Tracks LTS and current stable releases
71
71
  */
72
72
  node: {
73
- latest: '22',
74
- lts: ['22', '20', '18'],
75
- all: ['22', '20', '18'],
73
+ latest: '24',
74
+ lts: ['24', '22', '20', '18'],
75
+ all: ['24', '22', '20', '18'],
76
76
  deprecated: ['16', '14', '12', '10'],
77
77
  description: 'Supported Node.js versions - prefer LTS releases',
78
78
  // LTS information: https://nodejs.org/en/about/releases/
@@ -6,6 +6,14 @@ import { Generator } from '../core/generator.js';
6
6
 
7
7
  import { loadModulePrompts } from './loadModulePrompts.js';
8
8
 
9
+ function parseBooleanEnv(value) {
10
+ if (typeof value !== 'string') return null;
11
+ const normalized = value.trim().toLowerCase();
12
+ if (['1', 'true', 'yes', 'on'].includes(normalized)) return true;
13
+ if (['0', 'false', 'no', 'off'].includes(normalized)) return false;
14
+ return null;
15
+ }
16
+
9
17
  export async function runPrompts({ defaults = false } = {}) {
10
18
  const defaultsGit = getGitUserDefaults();
11
19
 
@@ -46,20 +54,7 @@ export async function runPrompts({ defaults = false } = {}) {
46
54
  message: 'Repository URL:',
47
55
  default: defaultsGit.gitRepository,
48
56
  },
49
- {
50
- type: 'list',
51
- name: 'phpVersion',
52
- message: 'PHP version:',
53
- choices: ['8.3', '8.2', '8.1'],
54
- default: '8.3',
55
- },
56
- {
57
- type: 'list',
58
- name: 'nodeVersion',
59
- message: 'Node.js version:',
60
- choices: ['22', '20', '18'],
61
- default: '22',
62
- },
57
+ // phpVersion and nodeVersion are locked to '8.4' / '24' — injected into context, not prompted
63
58
  {
64
59
  type: 'list',
65
60
  name: 'wpCoreVersion',
@@ -92,11 +87,24 @@ export async function runPrompts({ defaults = false } = {}) {
92
87
  const def = prompt.default;
93
88
  defaultAnswers[prompt.name] = typeof def === 'function' ? def() : def;
94
89
  }
90
+
91
+ const envEnableSage = parseBooleanEnv(process.env.WP_KIT_ENABLE_SAGE);
92
+ if (envEnableSage !== null) {
93
+ defaultAnswers.enable_sage = envEnableSage;
94
+ }
95
+
96
+ const envGitProvider = process.env.WP_KIT_GIT_PROVIDER;
97
+ if (envGitProvider) {
98
+ defaultAnswers.gitProvider = envGitProvider;
99
+ }
100
+
95
101
  const enabledModules = Object.keys(modulePromptDefs).filter(
96
102
  (mod) => defaultAnswers[`enable_${mod}`] === true
97
103
  );
98
104
  return {
99
105
  ...defaultAnswers,
106
+ phpVersion: '8.4',
107
+ nodeVersion: '24',
100
108
  enabledModules,
101
109
  projectDomain: `${defaultAnswers.projectName}.lndo.site`,
102
110
  salts: Generator.generateSalts(),
@@ -110,6 +118,8 @@ export async function runPrompts({ defaults = false } = {}) {
110
118
  );
111
119
  return {
112
120
  ...answers,
121
+ phpVersion: '8.4',
122
+ nodeVersion: '24',
113
123
  enabledModules,
114
124
  projectDomain: `${answers.projectName}.lndo.site`,
115
125
  salts: Generator.generateSalts(),
package/lib/utils/git.js CHANGED
@@ -23,16 +23,45 @@ export function getGitUserDefaults() {
23
23
  }
24
24
 
25
25
  /**
26
- * Clona codigodoleo/superpowers-sage e copia seu conteúdo para <targetDir>/.claude
26
+ * Dirs que cada destino copia do repositório superpowers-sage.
27
+ * A subpasta de destino dentro do projeto é TARGET_CONFIG[key].dest.
28
+ */
29
+ const TARGET_CONFIG = {
30
+ claude: {
31
+ dest: '.claude/plugins/superpowers-sage',
32
+ dirs: ['.claude-plugin', 'skills', 'agents', 'hooks'],
33
+ label: 'Claude Code (.claude/)',
34
+ },
35
+ cursor: {
36
+ dest: '.cursor/plugins/local/superpowers-sage',
37
+ dirs: ['.cursor-plugin', 'skills', 'agents', 'hooks', 'rules'],
38
+ label: 'Cursor (.cursor/)',
39
+ },
40
+ ai: {
41
+ dest: '.ai/superpowers-sage',
42
+ dirs: ['skills', 'agents', 'hooks'],
43
+ label: 'Agnóstico (.ai/)',
44
+ },
45
+ };
46
+
47
+ /**
48
+ * Clona codigodoleo/superpowers-sage e copia o conteúdo para os destinos selecionados.
27
49
  * Requer git instalado no host.
50
+ *
28
51
  * @param {string} targetDir - Caminho absoluto do projeto gerado
52
+ * @param {string[]} targets - Lista de destinos: 'claude' | 'cursor' | 'ai'
29
53
  */
30
- export async function installSuperpowersSage(targetDir) {
54
+ export async function installSuperpowersSage(targetDir, targets = ['claude']) {
31
55
  const repo = 'https://github.com/codigodoleo/superpowers-sage.git';
32
56
  const tmpDir = path.join(os.tmpdir(), `superpowers-sage-${Date.now()}`);
33
- const claudeDir = path.join(targetDir, '.claude');
34
57
 
35
- log(color.blue, '🔌 Installing superpowers-sage...');
58
+ const validTargets = targets.filter((t) => TARGET_CONFIG[t]);
59
+ if (validTargets.length === 0) {
60
+ log(color.orange, '[skills] Nenhum destino válido informado. Pulando.');
61
+ return;
62
+ }
63
+
64
+ log(color.blue, `🔌 Clonando superpowers-sage para: ${validTargets.join(', ')}...`);
36
65
 
37
66
  const result = spawnSync('git', ['clone', '--depth=1', repo, tmpDir], {
38
67
  stdio: 'inherit',
@@ -41,19 +70,27 @@ export async function installSuperpowersSage(targetDir) {
41
70
  if (result.status !== 0) {
42
71
  log(
43
72
  color.orange,
44
- '⚠️ Could not clone superpowers-sage (no network or git unavailable). Skipping.'
73
+ '⚠️ Não foi possível clonar superpowers-sage (sem rede ou git indisponível). Pulando.'
45
74
  );
46
75
  return;
47
76
  }
48
77
 
49
- // Copia o conteúdo completo do clone (exceto .git) para .claude do projeto
50
- await fs.ensureDir(claudeDir);
51
- await fs.copy(tmpDir, claudeDir, {
52
- overwrite: true,
53
- filter: (src) => !src.includes(`${path.sep}.git`),
54
- });
78
+ for (const key of validTargets) {
79
+ const cfg = TARGET_CONFIG[key];
80
+ const destBase = path.join(targetDir, cfg.dest);
55
81
 
56
- await fs.remove(tmpDir);
82
+ await fs.ensureDir(destBase);
83
+
84
+ for (const dir of cfg.dirs) {
85
+ const srcDir = path.join(tmpDir, dir);
86
+ if (!(await fs.pathExists(srcDir))) continue;
87
+
88
+ const destDir = path.join(destBase, dir);
89
+ await fs.copy(srcDir, destDir, { overwrite: true });
90
+ }
57
91
 
58
- log(color.green, '✅ superpowers-sage installed at .claude/');
92
+ log(color.green, `✅ superpowers-sage instalado em ${cfg.dest}`);
93
+ }
94
+
95
+ await fs.remove(tmpDir);
59
96
  }
@@ -0,0 +1,102 @@
1
+ # Reusable workflow — Docker Build & Push to Container Registry
2
+ #
3
+ # Como usar no seu workflow principal (.github/workflows/main.yml):
4
+ #
5
+ # on:
6
+ # push:
7
+ # branches: [main]
8
+ #
9
+ # jobs:
10
+ # deploy:
11
+ # uses: ./.github/workflows/deploy-docker.yml
12
+ # with:
13
+ # registry: ghcr.io # opcional — default: ghcr.io
14
+ # # registry: docker.io # para Docker Hub
15
+ # secrets:
16
+ # REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GHCR usa GITHUB_TOKEN
17
+ # # REGISTRY_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} # Docker Hub
18
+ #
19
+ # Secrets necessários (Settings → Secrets → Actions):
20
+ # REGISTRY_TOKEN — token de autenticação no registry (GITHUB_TOKEN é suficiente para GHCR)
21
+
22
+ name: Deploy (Docker)
23
+
24
+ on:
25
+ workflow_call:
26
+ inputs:
27
+ registry:
28
+ description: 'Container registry (ghcr.io ou docker.io)'
29
+ required: false
30
+ type: string
31
+ default: ghcr.io
32
+ image-name:
33
+ description: 'Nome da imagem (default: github.repository em lowercase)'
34
+ required: false
35
+ type: string
36
+ default: ''
37
+ dockerfile:
38
+ description: 'Caminho para o Dockerfile'
39
+ required: false
40
+ type: string
41
+ default: Dockerfile
42
+ context:
43
+ description: 'Contexto do build Docker'
44
+ required: false
45
+ type: string
46
+ default: '.'
47
+ secrets:
48
+ REGISTRY_TOKEN:
49
+ required: false
50
+
51
+ permissions:
52
+ contents: read
53
+ packages: write
54
+
55
+ jobs:
56
+ build-push:
57
+ name: Build & Push
58
+ runs-on: ubuntu-latest
59
+ steps:
60
+ - name: Checkout
61
+ uses: actions/checkout@v4
62
+
63
+ - name: Resolve image name
64
+ id: image
65
+ run: |
66
+ NAME="${{ inputs.image-name }}"
67
+ [ -z "$NAME" ] && NAME="${{ github.repository }}"
68
+ echo "name=${NAME,,}" >> $GITHUB_OUTPUT
69
+
70
+ - name: Set up QEMU
71
+ uses: docker/setup-qemu-action@v3
72
+
73
+ - name: Set up Docker Buildx
74
+ uses: docker/setup-buildx-action@v3
75
+
76
+ - name: Log in to registry
77
+ uses: docker/login-action@v3
78
+ with:
79
+ registry: ${{ inputs.registry }}
80
+ username: ${{ github.actor }}
81
+ password: ${{ secrets.REGISTRY_TOKEN || secrets.GITHUB_TOKEN }}
82
+
83
+ - name: Extract Docker metadata
84
+ id: meta
85
+ uses: docker/metadata-action@v5
86
+ with:
87
+ images: ${{ inputs.registry }}/${{ steps.image.outputs.name }}
88
+ tags: |
89
+ type=ref,event=branch
90
+ type=semver,pattern={{version}}
91
+ type=sha,prefix=sha-,format=short
92
+
93
+ - name: Build and push
94
+ uses: docker/build-push-action@v6
95
+ with:
96
+ context: ${{ inputs.context }}
97
+ file: ${{ inputs.dockerfile }}
98
+ push: true
99
+ tags: ${{ steps.meta.outputs.tags }}
100
+ labels: ${{ steps.meta.outputs.labels }}
101
+ cache-from: type=gha
102
+ cache-to: type=gha,mode=max
@@ -0,0 +1,126 @@
1
+ # Reusable workflow — SSH Deploy via rsync
2
+ #
3
+ # Como usar no seu workflow principal (.github/workflows/main.yml):
4
+ #
5
+ # on:
6
+ # push:
7
+ # branches: [main]
8
+ #
9
+ # jobs:
10
+ # deploy:
11
+ # uses: ./.github/workflows/deploy-ssh.yml
12
+ # with:
13
+ # host: meu-servidor.example.com
14
+ # user: deploy
15
+ # remote-path: /var/www/html
16
+ # theme-path: content/themes/meu-tema # opcional, para projetos com Sage
17
+ # secrets:
18
+ # SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
19
+ # SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
20
+ #
21
+ # Secrets necessários (Settings → Secrets → Actions):
22
+ # SSH_PRIVATE_KEY — chave SSH privada (gerada sem passphrase para CI)
23
+ # SSH_KNOWN_HOSTS — saída de: ssh-keyscan -H <host>
24
+ #
25
+ # Permissões no servidor — o rsync usa --no-perms --no-owner --no-group,
26
+ # portanto não altera permissões/proprietários existentes no destino.
27
+
28
+ name: Deploy (SSH + rsync)
29
+
30
+ on:
31
+ workflow_call:
32
+ inputs:
33
+ host:
34
+ description: 'Endereço do servidor de destino'
35
+ required: true
36
+ type: string
37
+ user:
38
+ description: 'Usuário SSH no servidor'
39
+ required: true
40
+ type: string
41
+ remote-path:
42
+ description: 'Caminho absoluto no servidor de destino'
43
+ required: true
44
+ type: string
45
+ php-version:
46
+ description: 'Versão do PHP para instalar dependências Composer'
47
+ required: false
48
+ type: string
49
+ default: '8.3'
50
+ node-version:
51
+ description: 'Versão do Node.js para build de assets'
52
+ required: false
53
+ type: string
54
+ default: '22'
55
+ theme-path:
56
+ description: 'Caminho relativo do tema para build (vazio = sem build de tema)'
57
+ required: false
58
+ type: string
59
+ default: ''
60
+ rsync-excludes:
61
+ description: 'Caminhos a excluir do rsync, separados por vírgula'
62
+ required: false
63
+ type: string
64
+ default: '.git,.env,node_modules/.cache'
65
+ secrets:
66
+ SSH_PRIVATE_KEY:
67
+ required: true
68
+ SSH_KNOWN_HOSTS:
69
+ required: true
70
+
71
+ jobs:
72
+ build-and-deploy:
73
+ name: Build & Deploy via SSH
74
+ runs-on: ubuntu-latest
75
+ steps:
76
+ - name: Checkout
77
+ uses: actions/checkout@v4
78
+ with:
79
+ fetch-depth: 0
80
+
81
+ - name: Setup PHP
82
+ uses: shivammathur/setup-php@v2
83
+ with:
84
+ php-version: ${{ inputs.php-version }}
85
+
86
+ - name: Install Composer dependencies
87
+ run: composer install --no-dev --optimize-autoloader --no-interaction
88
+
89
+ - name: Setup Node.js
90
+ if: inputs.theme-path != ''
91
+ uses: actions/setup-node@v4
92
+ with:
93
+ node-version: ${{ inputs.node-version }}
94
+ cache: 'npm'
95
+ cache-dependency-path: ${{ inputs.theme-path }}/package-lock.json
96
+
97
+ - name: Build theme assets
98
+ if: inputs.theme-path != ''
99
+ run: |
100
+ npm install --prefix "${{ inputs.theme-path }}"
101
+ npm run build --prefix "${{ inputs.theme-path }}"
102
+
103
+ - name: Configure SSH agent
104
+ uses: webfactory/ssh-agent@v0.9.0
105
+ with:
106
+ ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
107
+
108
+ - name: Add known hosts
109
+ run: |
110
+ mkdir -p ~/.ssh
111
+ echo "${{ secrets.SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
112
+ chmod 600 ~/.ssh/known_hosts
113
+
114
+ - name: Deploy via rsync
115
+ run: |
116
+ EXCLUDE_ARGS=""
117
+ IFS=',' read -ra ITEMS <<< "${{ inputs.rsync-excludes }}"
118
+ for item in "${ITEMS[@]}"; do
119
+ EXCLUDE_ARGS="$EXCLUDE_ARGS --exclude=${item// /}"
120
+ done
121
+
122
+ rsync -avz --delete \
123
+ $EXCLUDE_ARGS \
124
+ --no-perms --no-owner --no-group \
125
+ ./ \
126
+ "${{ inputs.user }}@${{ inputs.host }}:${{ inputs.remote-path }}/"
@@ -0,0 +1,55 @@
1
+ # Reusable workflow — Semantic Release
2
+ #
3
+ # Como usar no seu workflow principal (.github/workflows/main.yml):
4
+ #
5
+ # on:
6
+ # push:
7
+ # branches: [main]
8
+ #
9
+ # jobs:
10
+ # release:
11
+ # uses: ./.github/workflows/release.yml
12
+ # secrets:
13
+ # NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # opcional para projetos WordPress
14
+ #
15
+ # GITHUB_TOKEN é injetado automaticamente com os devidos escopos.
16
+
17
+ name: Semantic Release (reusable)
18
+
19
+ on:
20
+ workflow_call:
21
+ secrets:
22
+ NPM_TOKEN:
23
+ required: false
24
+
25
+ permissions:
26
+ contents: write
27
+ issues: write
28
+ pull-requests: write
29
+ id-token: write
30
+
31
+ jobs:
32
+ release:
33
+ name: Semantic Release
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - name: Checkout
37
+ uses: actions/checkout@v4
38
+ with:
39
+ fetch-depth: 0
40
+ persist-credentials: false
41
+
42
+ - name: Setup Node.js
43
+ uses: actions/setup-node@v4
44
+ with:
45
+ node-version: '24'
46
+ cache: 'npm'
47
+
48
+ - name: Install dependencies
49
+ run: npm ci
50
+
51
+ - name: Release
52
+ run: npx semantic-release
53
+ env:
54
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
55
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -12,6 +12,27 @@ export async function setupModule(context, generator) {
12
12
  if (context.gitProvider === 'gitlab') {
13
13
  await generator.generateFile('.gitlab/gitlab-ci.yml', context, 'deploy', '.gitlab-ci.yml');
14
14
  log(color.green, '[deploy] .gitlab-ci.yml gerado com sucesso.');
15
+
16
+ // Jobs reutilizáveis para .gitlab/ci/ (include: no pipeline principal)
17
+ await generator.generateFile(
18
+ '.gitlab/ci/release.yml',
19
+ context,
20
+ 'deploy',
21
+ '.gitlab/ci/release.yml'
22
+ );
23
+ await generator.generateFile(
24
+ '.gitlab/ci/deploy-docker.yml',
25
+ context,
26
+ 'deploy',
27
+ '.gitlab/ci/deploy-docker.yml'
28
+ );
29
+ await generator.generateFile(
30
+ '.gitlab/ci/deploy-ssh.yml',
31
+ context,
32
+ 'deploy',
33
+ '.gitlab/ci/deploy-ssh.yml'
34
+ );
35
+ log(color.green, '[deploy] Jobs reutilizáveis GitLab gerados em .gitlab/ci/');
15
36
  }
16
37
  if (context.gitProvider === 'github') {
17
38
  await generator.generateFile(
@@ -21,6 +42,24 @@ export async function setupModule(context, generator) {
21
42
  '.github/workflows/ci.yml'
22
43
  );
23
44
  log(color.green, '[deploy] GitHub Actions workflow gerado com sucesso.');
45
+
46
+ // Reusable workflows para .github/workflows/ (workflow_call no pipeline principal)
47
+ await generator.copyFile(
48
+ '.github/workflows/release.yml',
49
+ 'deploy',
50
+ '.github/workflows/release.yml'
51
+ );
52
+ await generator.copyFile(
53
+ '.github/workflows/deploy-docker.yml',
54
+ 'deploy',
55
+ '.github/workflows/deploy-docker.yml'
56
+ );
57
+ await generator.copyFile(
58
+ '.github/workflows/deploy-ssh.yml',
59
+ 'deploy',
60
+ '.github/workflows/deploy-ssh.yml'
61
+ );
62
+ log(color.green, '[deploy] Reusable workflows GitHub gerados em .github/workflows/');
24
63
  }
25
64
  if (context.gitProvider === 'bitbucket') {
26
65
  await generator.generateFile(
@@ -15,10 +15,8 @@ export default [
15
15
  value: 'docker',
16
16
  },
17
17
  {
18
- name: 'SSH',
18
+ name: 'SSH (rsync)',
19
19
  value: 'ssh',
20
- disabled: true,
21
- description: 'Em breve',
22
20
  },
23
21
  ],
24
22
  default: 'docker',
@@ -0,0 +1,35 @@
1
+ # Job reutilizável — Docker Build & Push to Container Registry
2
+ #
3
+ # Como usar no .gitlab-ci.yml principal:
4
+ #
5
+ # include:
6
+ # - local: '.gitlab/ci/deploy-docker.yml'
7
+ #
8
+ # stages:
9
+ # - deploy
10
+ #
11
+ # Variáveis necessárias (Settings → CI/CD → Variables):
12
+ # CI_REGISTRY_USER — automático para o GitLab Container Registry
13
+ # CI_REGISTRY_PASSWORD — automático para o GitLab Container Registry
14
+ # CI_REGISTRY_IMAGE — automático: registry.gitlab.com/<grupo>/<projeto>
15
+ #
16
+ # Para registry externo (Docker Hub, GHCR), sobrescreva as variáveis acima.
17
+
18
+ deploy:docker:
19
+ stage: deploy
20
+ image: docker:latest
21
+ services:
22
+ - docker:dind
23
+ variables:
24
+ DOCKER_TLS_CERTDIR: '/certs'
25
+ IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
26
+ IMAGE_LATEST: $CI_REGISTRY_IMAGE:latest
27
+ before_script:
28
+ - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
29
+ script:
30
+ - docker build --tag "$IMAGE_TAG" --tag "$IMAGE_LATEST" .
31
+ - docker push "$IMAGE_TAG"
32
+ - docker push "$IMAGE_LATEST"
33
+ rules:
34
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
35
+ - if: $CI_COMMIT_TAG