11 Commits

Author SHA1 Message Date
7d0ee0adea fix: resolve ShellCheck violations in shell scripts (#91)
- SC2155 (bin/localwp-setup.sh): declare local variables separately from
  command substitution assignments to avoid masking return values (13 fixes)
- SC2034 (bin/localwp-setup.sh): remove unused PLUGIN_TEXT_DOMAIN variable
- SC2162 (bin/localwp-setup.sh): add -r flag to read to avoid backslash mangling
- SC2154 (bin/setup-test-env.sh): add shellcheck disable for PHP variables in
  heredoc that ShellCheck incorrectly identifies as unassigned shell variables
- bin/setup-test-env.sh: remove self-modifying chmod +x $0 (unnecessary and
  bad practice; file permissions should be set once in version control)
- bin/setup-test-env.sh: change == to = in POSIX [ ] test expressions
- build.sh: add ./ prefix to directory glob copies for clarity
- build.sh: use subshell (cd build || exit 1; zip ...) instead of bare cd/cd..
  to avoid SC2103 and ensure working directory is always restored

Fixes part of #20 (shell script quality issues)
2026-03-18 11:47:38 +00:00
9fdfa7a8a9 fix: pin all GitHub Actions to full commit SHAs (resolves SonarCloud S7637) (#90)
Pin all floating version tags (@v1, @v2, @v3, @v4, @master) to full commit
SHAs across all workflow files to eliminate supply chain security risk.

Actions pinned:
- actions/checkout@v3 -> f43a0e5 (v3.6.0) in release.yml, sync-wiki.yml
- actions/checkout@v4 -> 34e1148 (v4) in tests.yml
- shivammathur/setup-php@v2 -> accd612 (v2) in all workflows
- actions/setup-node@v4 -> 49933ea (v4) in playground-tests*.yml, wordpress-tests.yml
- actions/upload-artifact@v4 -> ea165f8 (v4) in playground-tests*.yml, wordpress-tests.yml
- softprops/action-gh-release@v1 -> de2c0eb (v1) in release.yml
- codacy/codacy-analysis-cli-action@v4 -> 562ee3e (v4) in code-quality.yml
- github/codeql-action/upload-sarif@v3 -> 603b797 (v3) in code-quality.yml
- swissspidy/wp-performance-action@v2.0.3 -> b7e3ffc (v2.0.3) in playground-tests.yml
- SonarSource/sonarqube-scan-action@master -> 9598b8a in sonarcloud.yml

Closes #89
2026-03-18 04:58:57 +00:00
d6dcda908c fix: change comma-dangle rule to always-multiline per review feedback (#88)
Addresses Gemini review feedback from PR #66 (issue #73).
Changes 'comma-dangle' from 'never' to 'always-multiline' to improve
maintainability — cleaner git diffs and easier line reordering.
Updates admin/js files to comply with the new rule.

Closes #73
2026-03-17 21:00:19 +00:00
7640f01d0c quality-debt: document 782px WordPress mobile admin breakpoint (#87)
* fix: document 782px WordPress mobile admin breakpoint in media query

Adds an inline comment explaining that 782px is the WordPress mobile
admin breakpoint (wp-admin responsive threshold), addressing the
maintainability feedback from Gemini in PR #68.

Closes #69

* fix: add empty line before CSS comment to satisfy comment-empty-line-before rule

CodeFactor flagged missing empty line before the breakpoint comment (comment-empty-line-before).
Also simplifies comment text per Gemini Code Assist suggestion (removes redundant parenthetical).

Fixes CodeFactor FAILURE and Codacy ACTION_REQUIRED on PR #87.
2026-03-17 20:50:19 +00:00
632dda5952 fix: address PR #65 review feedback on error-checking-feedback-loops.md (#86)
- Fix yaml code block to bash at line 80 (shell command, not YAML)
- Remove useless cat pipe: use grep directly on test-output.log
- Remove useless cat pipe: use grep directly on phpcs-output.log

Closes #71
2026-03-17 20:45:53 +00:00
8fda3f1163 fix: add lint:js to quality script (#85)
Include JavaScript linting in the quality gate alongside PHP and CSS
linting, as suggested in PR #55 review feedback (Gemini, medium).

Closes #77
2026-03-17 20:37:05 +00:00
79f78882a6 fix: remove redundant existence check, use cy.within() for scoped selector (#84)
Addresses Gemini review feedback on PR #50 line 23: the .should('exist')
check was redundant since the if-condition already confirms element presence.
Refactored to use cy.within() to scope the .deactivate a check, eliminating
selector repetition and improving readability.

Closes #79
2026-03-17 20:29:00 +00:00
e1ee99ac9c fix: validate type parameter against allow-list in showMessage (#83)
Adds allow-list validation for the 'type' parameter in showMessage()
to prevent class injection vulnerabilities. The type is now checked
against ['success', 'error'] before being passed to addClass(), with
a safe fallback to 'error' for any unexpected values.

Addresses review feedback from PR #47 (gemini-code-assist finding).
Closes #76
2026-03-17 19:33:04 +00:00
6300f1c545 fix: remove duplicate deployment echo in build.sh (#80)
* fix: remove duplicate deployment echo in build.sh

Remove duplicate 'Deploying to local WordPress installation...' message.
Replace printf with echo "" for blank line separator to avoid \n
rendering issues without -e flag.

Closes #70

* fix: use ${WP_LOCAL_PLUGIN_DIR:-} for bash nounset safety in build.sh

Addresses CodeRabbit Major review feedback on PR #80.
Direct dereference of $WP_LOCAL_PLUGIN_DIR under set -u causes the script
to exit when the variable is unset, even though local deployment is optional.
Using ${WP_LOCAL_PLUGIN_DIR:-} safely handles the unset case without
triggering nounset.
2026-03-17 19:33:00 +00:00
c3738a3106 fix: remove stale /tmp/wordpress-develop before git clone (#82)
Prevents git clone failure on re-runs when the destination directory
already exists from a previous test suite installation.

Closes #74
2026-03-17 19:26:41 +00:00
e5d2994e40 docs: mention both WP-CLI network activation commands in Playground-Testing.md (#81)
Address review feedback from PR #63 (gemini-code-assist): document both
`wp plugin activate --network` (for installed plugins) and
`wp plugin install --activate-network` (to install and activate in one step).

Closes #72
2026-03-17 19:22:48 +00:00
21 changed files with 386 additions and 376 deletions

View File

@@ -77,7 +77,7 @@ uses: actions/upload-artifact@v4
**Solution**: Use port 80 for multisite environments: **Solution**: Use port 80 for multisite environments:
```yaml ```bash
npx @wp-playground/cli server --blueprint playground/multisite-blueprint.json --port 80 --login & npx @wp-playground/cli server --blueprint playground/multisite-blueprint.json --port 80 --login &
``` ```
@@ -142,7 +142,7 @@ npm run test:playground:multisite
2. **Analyze Output for Errors**: 2. **Analyze Output for Errors**:
```bash ```bash
cat test-output.log | grep -i 'error\|fail\|exception' grep -i 'error\|fail\|exception' test-output.log
``` ```
3. **Parse Structured Test Results** (if available): 3. **Parse Structured Test Results** (if available):
@@ -221,7 +221,7 @@ npm run lint:css
2. **Analyze Output for Errors**: 2. **Analyze Output for Errors**:
```bash ```bash
cat phpcs-output.log | grep -i 'ERROR\|WARNING' grep -i 'ERROR\|WARNING' phpcs-output.log
``` ```
3. **Automatically Fix Issues** (when possible): 3. **Automatically Fix Issues** (when possible):

View File

@@ -16,7 +16,7 @@ module.exports = {
sourceType: 'module' sourceType: 'module'
}, },
rules: { rules: {
'comma-dangle': ['error', 'never'], 'comma-dangle': ['error', 'always-multiline'],
'no-console': 'warn', 'no-console': 'warn',
'no-unused-vars': 'warn' 'no-unused-vars': 'warn'
}, },

View File

@@ -20,7 +20,7 @@ jobs:
clean: 'true' clean: 'true'
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with: with:
php-version: '8.1' php-version: '8.1'
extensions: mbstring, intl, zip extensions: mbstring, intl, zip
@@ -47,7 +47,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with: with:
php-version: '8.1' php-version: '8.1'
extensions: mbstring, intl, zip extensions: mbstring, intl, zip
@@ -68,7 +68,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with: with:
php-version: '8.1' php-version: '8.1'
extensions: mbstring, intl, zip extensions: mbstring, intl, zip
@@ -119,7 +119,7 @@ jobs:
# #
# - name: SonarCloud Scan # - name: SonarCloud Scan
# if: steps.check_sonar_token.outputs.skip != 'true' # if: steps.check_sonar_token.outputs.skip != 'true'
# uses: SonarSource/sonarqube-scan-action@master # uses: SonarSource/sonarqube-scan-action@9598b8a83feef37de07f549027fab50ecffe6a6e # master
# env: # env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
@@ -143,10 +143,8 @@ jobs:
- name: Check if Codacy token is set - name: Check if Codacy token is set
id: check_codacy_token id: check_codacy_token
env:
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
run: | run: |
if [ -z "$CODACY_PROJECT_TOKEN" ]; then if [ -z "${{ secrets.CODACY_PROJECT_TOKEN }}" ]; then
echo "CODACY_PROJECT_TOKEN is not set, running Codacy without upload" echo "CODACY_PROJECT_TOKEN is not set, running Codacy without upload"
echo "skip_upload=true" >> $GITHUB_OUTPUT echo "skip_upload=true" >> $GITHUB_OUTPUT
else else
@@ -154,7 +152,7 @@ jobs:
fi fi
- name: Run Codacy Analysis CLI - name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@v4 uses: codacy/codacy-analysis-cli-action@562ee3e92b8e92df8b67e0a5ff8aa8e261919c08 # v4
with: with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }} project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
verbose: true verbose: true
@@ -169,7 +167,7 @@ jobs:
- name: Upload SARIF results file - name: Upload SARIF results file
if: steps.check_codacy_token.outputs.skip_upload != 'true' if: steps.check_codacy_token.outputs.skip_upload != 'true'
uses: github/codeql-action/upload-sarif@v3 uses: github/codeql-action/upload-sarif@603b797f8b14b413fe025cd935a91c16c4782713 # v3
with: with:
sarif_file: results.sarif sarif_file: results.sarif
continue-on-error: true continue-on-error: true

View File

@@ -42,7 +42,7 @@ jobs:
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
extensions: dom, curl, libxml, mbstring, zip, pdo, mysql, pdo_mysql, bcmath, soap, intl, gd, exif, iconv extensions: dom, curl, libxml, mbstring, zip, pdo, mysql, pdo_mysql, bcmath, soap, intl, gd, exif, iconv

View File

@@ -36,7 +36,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: with:
node-version: '20' node-version: '20'
cache: 'npm' cache: 'npm'
@@ -98,7 +98,7 @@ jobs:
- name: Upload Cypress artifacts - name: Upload Cypress artifacts
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: cypress-playground-results name: cypress-playground-results
path: | path: |

View File

@@ -40,7 +40,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'npm' cache: 'npm'
@@ -67,7 +67,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: with:
node-version: '20' node-version: '20'
cache: 'npm' cache: 'npm'
@@ -142,7 +142,7 @@ jobs:
- name: Upload Cypress artifacts - name: Upload Cypress artifacts
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: cypress-single-site-results name: cypress-single-site-results
path: | path: |
@@ -163,7 +163,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: with:
node-version: '20' node-version: '20'
cache: 'npm' cache: 'npm'
@@ -239,7 +239,7 @@ jobs:
- name: Upload Cypress artifacts - name: Upload Cypress artifacts
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: cypress-multisite-results name: cypress-multisite-results
path: | path: |
@@ -261,7 +261,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: WordPress Performance Tests - name: WordPress Performance Tests
uses: swissspidy/wp-performance-action@v2.0.3 uses: swissspidy/wp-performance-action@b7e3ffcf0fc4a48b62492e021e0ebeb51430ff11 # v2.0.3
with: with:
plugins: | plugins: |
./ ./

View File

@@ -14,10 +14,10 @@ jobs:
contents: write contents: write
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with: with:
php-version: '7.4' php-version: '7.4'
extensions: mbstring, intl, zip extensions: mbstring, intl, zip
@@ -62,7 +62,7 @@ jobs:
- name: Create Release - name: Create Release
id: create_release id: create_release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1
with: with:
files: wp-plugin-starter-template-for-ai-coding-${{ env.VERSION }}.zip files: wp-plugin-starter-template-for-ai-coding-${{ env.VERSION }}.zip
name: v${{ env.VERSION }} - WordPress Plugin Starter Template name: v${{ env.VERSION }} - WordPress Plugin Starter Template

View File

@@ -39,10 +39,8 @@ jobs:
- name: Check if SonarCloud token is set - name: Check if SonarCloud token is set
id: check_token id: check_token
env:
SONARCLOUD_GITHUB: ${{ secrets.SONARCLOUD_GITHUB }}
run: | run: |
if [ -z "$SONARCLOUD_GITHUB" ]; then if [ -z "${{ secrets.SONARCLOUD_GITHUB }}" ]; then
echo "SONARCLOUD_GITHUB is not set, skipping SonarCloud analysis" echo "SONARCLOUD_GITHUB is not set, skipping SonarCloud analysis"
echo "skip=true" >> $GITHUB_OUTPUT echo "skip=true" >> $GITHUB_OUTPUT
else else
@@ -51,7 +49,7 @@ jobs:
- name: SonarCloud Scan - name: SonarCloud Scan
if: steps.check_token.outputs.skip != 'true' if: steps.check_token.outputs.skip != 'true'
uses: SonarSource/sonarqube-scan-action@master uses: SonarSource/sonarqube-scan-action@9598b8a83feef37de07f549027fab50ecffe6a6e # master
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONARCLOUD_GITHUB }} SONAR_TOKEN: ${{ secrets.SONARCLOUD_GITHUB }}

View File

@@ -15,7 +15,7 @@ jobs:
contents: write contents: write
steps: steps:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v3 uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
- name: Configure Git - name: Configure Git
run: | run: |
@@ -27,10 +27,6 @@ jobs:
git clone https://github.com/${{ github.repository }}.wiki.git wiki git clone https://github.com/${{ github.repository }}.wiki.git wiki
- name: Sync wiki content - name: Sync wiki content
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: | run: |
# Remove all files from wiki repository except .git # Remove all files from wiki repository except .git
find wiki -mindepth 1 -maxdepth 1 -not -name '.git' -exec rm -rf {} \; find wiki -mindepth 1 -maxdepth 1 -not -name '.git' -exec rm -rf {} \;
@@ -54,4 +50,4 @@ jobs:
git commit -m "Sync wiki from source repository" git commit -m "Sync wiki from source repository"
# Push changes # Push changes
git push https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY.wiki.git git push https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.wiki.git

View File

@@ -17,12 +17,12 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with: with:
clean: 'true' clean: 'true'
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with: with:
php-version: ${{ matrix.php-versions }} php-version: ${{ matrix.php-versions }}
extensions: mbstring, intl, zip extensions: mbstring, intl, zip
@@ -43,12 +43,12 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with: with:
clean: 'true' clean: 'true'
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
with: with:
php-version: '7.4' php-version: '7.4'
extensions: mbstring, intl, zip extensions: mbstring, intl, zip

View File

@@ -37,7 +37,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'npm' cache: 'npm'
@@ -73,7 +73,7 @@ jobs:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: with:
node-version: '20' node-version: '20'
cache: 'npm' cache: 'npm'
@@ -124,7 +124,7 @@ jobs:
- name: Upload Cypress artifacts - name: Upload Cypress artifacts
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: cypress-results name: cypress-results
path: | path: |

View File

@@ -102,7 +102,7 @@ In a WordPress multisite environment, there are two ways to activate plugins:
1. **Network Activation**: Activates a plugin for all sites in the network 1. **Network Activation**: Activates a plugin for all sites in the network
* In the WordPress admin, go to Network Admin > Plugins * In the WordPress admin, go to Network Admin > Plugins
* Click "Network Activate" under the plugin * Click "Network Activate" under the plugin
* Or use WP-CLI: `wp plugin activate plugin-name --network` * Or use WP-CLI: `wp plugin activate plugin-name --network` (for installed plugins), or `wp plugin install plugin-name --activate-network` (to install and activate)
2. **Per-Site Activation**: Activates a plugin for a specific site 2. **Per-Site Activation**: Activates a plugin for a specific site
* In the WordPress admin, go to the specific site's admin area * In the WordPress admin, go to the specific site's admin area

View File

@@ -124,6 +124,8 @@
} }
/* Responsive Styles */ /* Responsive Styles */
/* 782px is the WordPress mobile admin breakpoint. */
@media screen and (max-width: 782px) { @media screen and (max-width: 782px) {
.wpst-form-table th { .wpst-form-table th {
width: 100%; width: 100%;

View File

@@ -78,7 +78,7 @@
data: { data: {
action: 'wpst_save_settings', action: 'wpst_save_settings',
nonce: wpstData.nonce, nonce: wpstData.nonce,
formData: formData formData: formData,
}, },
success: function (response) { success: function (response) {
if (response.success) { if (response.success) {
@@ -93,8 +93,8 @@
complete: function () { complete: function () {
// Re-enable submit button and remove loading state. // Re-enable submit button and remove loading state.
$submitButton.prop( 'disabled', false ).removeClass( 'loading' ); $submitButton.prop( 'disabled', false ).removeClass( 'loading' );
} },
} },
); );
}, },
@@ -123,19 +123,19 @@
300, 300,
function () { function () {
$( this ).remove(); $( this ).remove();
} },
); );
}, },
5000 5000,
); );
} },
}; };
// Initialize when document is ready. // Initialize when document is ready.
$( document ).ready( $( document ).ready(
function () { function () {
WPSTAdmin.init(); WPSTAdmin.init();
} },
); );
})( jQuery ); })( jQuery );

View File

@@ -48,7 +48,7 @@
if ($( e.target ).hasClass( 'wpst-modal' )) { if ($( e.target ).hasClass( 'wpst-modal' )) {
WPSTUpdateSourceSelector.closeModal(); WPSTUpdateSourceSelector.closeModal();
} }
} },
); );
// Select source option. // Select source option.
@@ -116,7 +116,7 @@
data: { data: {
action: 'wpst_set_update_source', // AJAX action hook. action: 'wpst_set_update_source', // AJAX action hook.
nonce: wpstModalData.nonce, // Security nonce. nonce: wpstModalData.nonce, // Security nonce.
source: this.selectedSource source: this.selectedSource,
}, },
success: function (response) { success: function (response) {
if (response.success) { if (response.success) {
@@ -127,7 +127,7 @@
function () { function () {
WPSTUpdateSourceSelector.closeModal(); WPSTUpdateSourceSelector.closeModal();
}, },
1500 1500,
); );
} else { } else {
WPSTUpdateSourceSelector.showMessage( 'error', response.data.message ); WPSTUpdateSourceSelector.showMessage( 'error', response.data.message );
@@ -139,8 +139,8 @@
complete: function () { complete: function () {
// Reset button state. // Reset button state.
$saveButton.prop( 'disabled', false ).text( wpstModalData.i18n.confirm ); $saveButton.prop( 'disabled', false ).text( wpstModalData.i18n.confirm );
} },
} },
); );
}, },
@@ -153,26 +153,30 @@
showMessage: function (type, message) { showMessage: function (type, message) {
const $message = this.$modal.find( '.wpst-modal-message' ); const $message = this.$modal.find( '.wpst-modal-message' );
// Set message as plain text to prevent XSS, then apply type class. // Validate type against allow-list to prevent class injection vulnerabilities.
$message.text( message ).removeClass( 'success error' ).addClass( type ).show(); const allowedTypes = [ 'success', 'error' ];
const safeType = allowedTypes.includes( type ) ? type : 'error';
// Set message as plain text to prevent XSS, then apply validated type class.
$message.text( message ).removeClass( 'success error' ).addClass( safeType ).show();
// Hide message after a delay for success messages. // Hide message after a delay for success messages.
if (type === 'success') { if (safeType === 'success') {
setTimeout( setTimeout(
function () { function () {
$message.fadeOut( 300 ); $message.fadeOut( 300 );
}, },
3000 3000,
); );
} }
} },
}; };
// Initialize when document is ready. // Initialize when document is ready.
$( document ).ready( $( document ).ready(
function () { function () {
WPSTUpdateSourceSelector.init(); WPSTUpdateSourceSelector.init();
} },
); );
})( jQuery ); })( jQuery );

View File

@@ -117,6 +117,7 @@ install_test_suite() {
# set up testing suite if it doesn't yet exist # set up testing suite if it doesn't yet exist
if [ ! -d "$WP_TESTS_DIR" ]; then if [ ! -d "$WP_TESTS_DIR" ]; then
mkdir -p "$WP_TESTS_DIR" mkdir -p "$WP_TESTS_DIR"
rm -rf /tmp/wordpress-develop
if ! git clone --quiet --depth=1 --branch "$GIT_REF" https://github.com/WordPress/wordpress-develop.git /tmp/wordpress-develop; then if ! git clone --quiet --depth=1 --branch "$GIT_REF" https://github.com/WordPress/wordpress-develop.git /tmp/wordpress-develop; then
echo "Error: Failed to clone wordpress-develop at branch/tag $GIT_REF" >&2 echo "Error: Failed to clone wordpress-develop at branch/tag $GIT_REF" >&2
exit 1 exit 1

View File

@@ -28,7 +28,6 @@ set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")" PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
PLUGIN_SLUG="wp-plugin-starter-template" PLUGIN_SLUG="wp-plugin-starter-template"
PLUGIN_TEXT_DOMAIN="wp-plugin-starter-template"
# LocalWP paths (macOS) # LocalWP paths (macOS)
LOCAL_SITES_DIR="$HOME/Local Sites" LOCAL_SITES_DIR="$HOME/Local Sites"
@@ -83,7 +82,8 @@ check_localwp() {
exit 1 exit 1
fi fi
local version=$("$LOCAL_WP_CLI" --version 2>/dev/null || echo "unknown") local version
version=$("$LOCAL_WP_CLI" --version 2>/dev/null || echo "unknown")
log_info "LocalWP WP-CLI version: $version" log_info "LocalWP WP-CLI version: $version"
} }
@@ -96,7 +96,8 @@ get_site_path() {
# Get WordPress path within site # Get WordPress path within site
get_wp_path() { get_wp_path() {
local site_name="$1" local site_name="$1"
local site_path=$(get_site_path "$site_name") local site_path
site_path=$(get_site_path "$site_name")
# LocalWP uses app/public for WordPress files # LocalWP uses app/public for WordPress files
echo "$site_path/app/public" echo "$site_path/app/public"
@@ -105,21 +106,24 @@ get_wp_path() {
# Check if site exists # Check if site exists
site_exists() { site_exists() {
local site_name="$1" local site_name="$1"
local site_path=$(get_site_path "$site_name") local site_path
site_path=$(get_site_path "$site_name")
[ -d "$site_path" ] [ -d "$site_path" ]
} }
# Get plugin destination path # Get plugin destination path
get_plugin_path() { get_plugin_path() {
local site_name="$1" local site_name="$1"
local wp_path=$(get_wp_path "$site_name") local wp_path
wp_path=$(get_wp_path "$site_name")
echo "$wp_path/wp-content/plugins/$PLUGIN_SLUG" echo "$wp_path/wp-content/plugins/$PLUGIN_SLUG"
} }
# Sync plugin files to LocalWP site # Sync plugin files to LocalWP site
sync_plugin() { sync_plugin() {
local site_name="$1" local site_name="$1"
local plugin_dest=$(get_plugin_path "$site_name") local plugin_dest
plugin_dest=$(get_plugin_path "$site_name")
if ! site_exists "$site_name"; then if ! site_exists "$site_name"; then
log_error "Site '$site_name' does not exist" log_error "Site '$site_name' does not exist"
@@ -181,7 +185,8 @@ create_site() {
check_localwp check_localwp
local site_path=$(get_site_path "$site_name") local site_path
site_path=$(get_site_path "$site_name")
if site_exists "$site_name"; then if site_exists "$site_name"; then
log_warning "Site '$site_name' already exists at: $site_path" log_warning "Site '$site_name' already exists at: $site_path"
@@ -230,7 +235,7 @@ create_site() {
echo "" echo ""
# Wait for user to create site # Wait for user to create site
read -p "Press Enter after you've created the site in LocalWP..." read -r -p "Press Enter after you've created the site in LocalWP..."
if site_exists "$site_name"; then if site_exists "$site_name"; then
log_success "Site detected at: $site_path" log_success "Site detected at: $site_path"
@@ -250,7 +255,8 @@ create_site() {
# Install recommended plugins (matching Playground blueprint) # Install recommended plugins (matching Playground blueprint)
install_recommended_plugins() { install_recommended_plugins() {
local site_name="$1" local site_name="$1"
local wp_path=$(get_wp_path "$site_name") local wp_path
wp_path=$(get_wp_path "$site_name")
log_info "Note: Install these plugins to match Playground environment:" log_info "Note: Install these plugins to match Playground environment:"
echo " - Plugin Toggle (plugin-toggle)" echo " - Plugin Toggle (plugin-toggle)"
@@ -265,8 +271,10 @@ show_site_info() {
local domain="$2" local domain="$2"
local multisite="$3" local multisite="$3"
local site_path=$(get_site_path "$site_name") local site_path
local plugin_path=$(get_plugin_path "$site_name") site_path=$(get_site_path "$site_name")
local plugin_path
plugin_path=$(get_plugin_path "$site_name")
echo "" echo ""
echo "============================================" echo "============================================"
@@ -305,7 +313,8 @@ reset_site() {
echo echo
if [[ $REPLY =~ ^[Yy]$ ]]; then if [[ $REPLY =~ ^[Yy]$ ]]; then
local plugin_path=$(get_plugin_path "$site_name") local plugin_path
plugin_path=$(get_plugin_path "$site_name")
log_info "Removing plugin files..." log_info "Removing plugin files..."
rm -rf "$plugin_path" rm -rf "$plugin_path"
@@ -345,14 +354,16 @@ show_info() {
echo "===============================" echo "==============================="
for site_name in "$SINGLE_SITE_NAME" "$MULTISITE_NAME"; do for site_name in "$SINGLE_SITE_NAME" "$MULTISITE_NAME"; do
local site_path=$(get_site_path "$site_name") local site_path
site_path=$(get_site_path "$site_name")
if site_exists "$site_name"; then if site_exists "$site_name"; then
echo "" echo ""
echo " ${GREEN}${NC} $site_name" echo " ${GREEN}${NC} $site_name"
echo " Path: $site_path" echo " Path: $site_path"
local plugin_path=$(get_plugin_path "$site_name") local plugin_path
plugin_path=$(get_plugin_path "$site_name")
if [ -d "$plugin_path" ]; then if [ -d "$plugin_path" ]; then
echo " Plugin: ${GREEN}Installed${NC}" echo " Plugin: ${GREEN}Installed${NC}"
else else
@@ -375,21 +386,21 @@ show_info() {
# Main command handler # Main command handler
case "${1:-}" in case "${1:-}" in
create) create)
shift shift
create_site "$@" create_site "$@"
;; ;;
sync) sync)
sync_all sync_all
;; ;;
reset) reset)
shift shift
reset_site "$@" reset_site "$@"
;; ;;
info) info)
show_info show_info
;; ;;
*) *)
echo "LocalWP Integration Script" echo "LocalWP Integration Script"
echo "" echo ""
echo "Usage:" echo "Usage:"

View File

@@ -1,8 +1,5 @@
#!/bin/bash #!/bin/bash
# Make this script executable
chmod +x "$0"
# Check if environment type is provided # Check if environment type is provided
if [ -z "$1" ]; then if [ -z "$1" ]; then
echo "Usage: $0 [single|multisite|playground-single|playground-multisite]" echo "Usage: $0 [single|multisite|playground-single|playground-multisite]"
@@ -49,7 +46,7 @@ install_wp_playground() {
fi fi
} }
if [ "$ENV_TYPE" == "single" ]; then if [ "$ENV_TYPE" = "single" ]; then
echo "Setting up single site environment..." echo "Setting up single site environment..."
# Install wp-env if needed # Install wp-env if needed
@@ -83,7 +80,7 @@ if [ "$ENV_TYPE" == "single" ]; then
echo "Site: http://localhost:8888" echo "Site: http://localhost:8888"
echo "Admin login: admin / password" echo "Admin login: admin / password"
elif [ "$ENV_TYPE" == "multisite" ]; then elif [ "$ENV_TYPE" = "multisite" ]; then
echo "Setting up multisite environment..." echo "Setting up multisite environment..."
# Install wp-env if needed # Install wp-env if needed
@@ -124,7 +121,7 @@ elif [ "$ENV_TYPE" == "multisite" ]; then
echo "Test site: http://localhost:8888/testsite" echo "Test site: http://localhost:8888/testsite"
echo "Admin login: admin / password" echo "Admin login: admin / password"
elif [ "$ENV_TYPE" == "playground-single" ]; then elif [ "$ENV_TYPE" = "playground-single" ]; then
echo "Setting up WordPress Playground single site environment..." echo "Setting up WordPress Playground single site environment..."
# Install wp-playground if needed # Install wp-playground if needed
@@ -193,7 +190,7 @@ EOF
echo "Admin login: admin / password" echo "Admin login: admin / password"
echo "Press Ctrl+C to stop the server when done." echo "Press Ctrl+C to stop the server when done."
elif [ "$ENV_TYPE" == "playground-multisite" ]; then elif [ "$ENV_TYPE" = "playground-multisite" ]; then
echo "Setting up WordPress Playground multisite environment..." echo "Setting up WordPress Playground multisite environment..."
# Install wp-playground if needed # Install wp-playground if needed
@@ -205,6 +202,7 @@ elif [ "$ENV_TYPE" == "playground-multisite" ]; then
zip -r dist/plugin.zip . -x "node_modules/*" "dist/*" ".git/*" zip -r dist/plugin.zip . -x "node_modules/*" "dist/*" ".git/*"
# Update blueprint to use local plugin # Update blueprint to use local plugin
# shellcheck disable=SC2154
cat >playground/multisite-blueprint.json <<EOF cat >playground/multisite-blueprint.json <<EOF
{ {
"landingPage": "/wp-admin/network/", "landingPage": "/wp-admin/network/",

View File

@@ -46,9 +46,9 @@ cp -R README.md LICENSE CHANGELOG.md readme.txt composer.json "$BUILD_DIR/"
# Copy directories # Copy directories
echo "Copying directories..." echo "Copying directories..."
mkdir -p "$BUILD_DIR/admin" "$BUILD_DIR/includes" "$BUILD_DIR/languages" "$BUILD_DIR/assets" mkdir -p "$BUILD_DIR/admin" "$BUILD_DIR/includes" "$BUILD_DIR/languages" "$BUILD_DIR/assets"
cp -R admin/* "$BUILD_DIR/admin/" cp -R ./admin/* "$BUILD_DIR/admin/"
cp -R includes/* "$BUILD_DIR/includes/" cp -R ./includes/* "$BUILD_DIR/includes/"
cp -R languages/* "$BUILD_DIR/languages/" cp -R ./languages/* "$BUILD_DIR/languages/"
# Create assets directory structure # Create assets directory structure
mkdir -p "$BUILD_DIR/assets/banner" "$BUILD_DIR/assets/icon" "$BUILD_DIR/assets/screenshots" mkdir -p "$BUILD_DIR/assets/banner" "$BUILD_DIR/assets/icon" "$BUILD_DIR/assets/screenshots"
@@ -73,9 +73,10 @@ fi
# Create ZIP file. # Create ZIP file.
echo "Creating ZIP file..." echo "Creating ZIP file..."
cd build || exit 1 (
zip -r "../$ZIP_FILE" "$PLUGIN_SLUG" -x "*.DS_Store" -x "*.git*" -x "*.github*" cd build || exit 1
cd .. zip -r "../$ZIP_FILE" "$PLUGIN_SLUG" -x "*.DS_Store" -x "*.git*" -x "*.github*"
)
# Check if ZIP file was created successfully # Check if ZIP file was created successfully
if [ -f "$ZIP_FILE" ]; then if [ -f "$ZIP_FILE" ]; then
@@ -83,8 +84,8 @@ if [ -f "$ZIP_FILE" ]; then
echo "File path: $(pwd)/$ZIP_FILE" echo "File path: $(pwd)/$ZIP_FILE"
# Deploy to local WordPress installation if environment variable is set # Deploy to local WordPress installation if environment variable is set
if [ -n "$WP_LOCAL_PLUGIN_DIR" ]; then if [ -n "${WP_LOCAL_PLUGIN_DIR:-}" ]; then
printf '\nDeploying to local WordPress installation...\n' echo ""
echo "Deploying to local WordPress installation..." echo "Deploying to local WordPress installation..."
# Remove existing plugin directory. # Remove existing plugin directory.

View File

@@ -20,8 +20,9 @@ describe('WordPress Playground Single Site Tests', () => {
cy.get('body', { timeout: 15000 }).then(($body) => { cy.get('body', { timeout: 15000 }).then(($body) => {
// Verify the starter template plugin exists and is activated. // Verify the starter template plugin exists and is activated.
if ($body.find('tr[data-slug="wp-plugin-starter-template-for-ai-coding"]').length) { if ($body.find('tr[data-slug="wp-plugin-starter-template-for-ai-coding"]').length) {
cy.get('tr[data-slug="wp-plugin-starter-template-for-ai-coding"]').should('exist'); cy.get('tr[data-slug="wp-plugin-starter-template-for-ai-coding"]').within(() => {
cy.get('tr[data-slug="wp-plugin-starter-template-for-ai-coding"] .deactivate a').should('exist'); cy.get('.deactivate a').should('exist');
});
} else { } else {
cy.log('Starter template plugin not found by slug, skipping check'); cy.log('Starter template plugin not found by slug, skipping check');
} }

View File

@@ -39,7 +39,7 @@
"test:php": "composer run-script test", "test:php": "composer run-script test",
"lint": "composer run-script lint", "lint": "composer run-script lint",
"fix": "composer run-script fix", "fix": "composer run-script fix",
"quality": "npm run lint && npm run lint:css && npm run test:php" "quality": "npm run lint && npm run lint:js && npm run lint:css && npm run test:php"
}, },
"repository": { "repository": {
"type": "git", "type": "git",