From 371e17c21800c4dfee796c9087fab423fc1d6594 Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Mon, 7 Sep 2026 14:04:50 +0500 Subject: [PATCH 1/7] app file validation --- .github/workflows/validate-apps.yml | 88 +++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/validate-apps.yml diff --git a/.github/workflows/validate-apps.yml b/.github/workflows/validate-apps.yml new file mode 100644 index 000000000..f764ccf3f --- /dev/null +++ b/.github/workflows/validate-apps.yml @@ -0,0 +1,88 @@ +name: Validate app files + +on: + pull_request: + paths: + - 'apps/**' + +permissions: + contents: read + pull-requests: write + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Get modified app files + id: changed + run: | + FILES=$(git diff --name-only --diff-filter=ACMR origin/${{ github.base_ref }}...HEAD -- 'apps/*' | grep -v '\.template$' || true) + echo "files<> $GITHUB_OUTPUT + echo "$FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Download pla-site-tool + if: steps.changed.outputs.files != '' + run: | + wget -O pla-site-tool "$(curl -Ls https://api.github.com/repos/kazam0180/portable-apps/releases | sed 's/[()",{} ]/\n/g' | grep -oi "https.*pla-site-tool.*mage$" | head -1)" + chmod +x pla-site-tool + + - name: Validate files + if: steps.changed.outputs.files != '' + id: validate + run: | + FAILED="" + while IFS= read -r file; do + [ -z "$file" ] && continue + echo "Validating: $file" + if ! ./pla-site-tool -i "$file" 2>&1; then + FAILED="${FAILED}${file}\n" + else + rm -f "${file}.json" + fi + done <<< "${{ steps.changed.outputs.files }}" + + if [ -n "$FAILED" ]; then + echo "failed<> $GITHUB_OUTPUT + echo -e "$FAILED" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "has_failed=true" >> $GITHUB_OUTPUT + else + echo "has_failed=false" >> $GITHUB_OUTPUT + fi + + - name: Comment on PR + if: steps.validate.outputs.has_failed == 'true' + uses: actions/github-script@v7 + with: + script: | + const failedFiles = `${{ steps.validate.outputs.failed }}`.trim().split('\n').filter(f => f); + const body = `### App file validation failed\n\nThe following files could not be converted to JSON:\n\n${failedFiles.map(f => `- \`${f}\``).join('\n')}\n\nPlease check the file format against [\`apps/.template\`](apps/.template) and fix any syntax errors.`; + + // Find and update existing comment or create new one + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(c => c.user.type === 'Bot' && c.body.includes('App file validation failed')); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } From 53fb9e64a2e5df8488a2eb117256db0605782a9f Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Mon, 7 Sep 2026 14:06:02 +0500 Subject: [PATCH 2/7] test --- apps/testappp | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/testappp diff --git a/apps/testappp b/apps/testappp new file mode 100644 index 000000000..57106caac --- /dev/null +++ b/apps/testappp @@ -0,0 +1 @@ +new app From a62d42f00c761bda200f2ad698d319d74b1b0b14 Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Mon, 7 Sep 2026 14:04:50 +0500 Subject: [PATCH 3/7] app file validation --- .github/workflows/validate-apps.yml | 90 +++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 .github/workflows/validate-apps.yml diff --git a/.github/workflows/validate-apps.yml b/.github/workflows/validate-apps.yml new file mode 100644 index 000000000..9ffa1bb3d --- /dev/null +++ b/.github/workflows/validate-apps.yml @@ -0,0 +1,90 @@ +name: Validate app files + +on: + pull_request: + paths: + - 'apps/**' + +permissions: + contents: read + pull-requests: write + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Get modified app files + id: changed + run: | + FILES=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }}...HEAD -- 'apps/*' | grep -v '\.template$' || true) + echo "files<> $GITHUB_OUTPUT + echo "$FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Download pla-site-tool + if: steps.changed.outputs.files != '' + run: | + wget -O pla-site-tool "$(curl -Ls https://api.github.com/repos/kazam0180/portable-apps/releases | sed 's/[()",{} ]/\n/g' | grep -oi "https.*pla-site-tool.*mage$" | head -1)" + chmod +x pla-site-tool + + - name: Validate files + if: steps.changed.outputs.files != '' + id: validate + run: | + FAILED="" + while IFS= read -r file; do + [ -z "$file" ] && continue + echo "Validating: $file" + if ! ./pla-site-tool -i "$file" 2>&1; then + FAILED="${FAILED}${file}\n" + else + rm -f "${file}.json" + fi + done <<< "${{ steps.changed.outputs.files }}" + + if [ -n "$FAILED" ]; then + echo "failed<> $GITHUB_OUTPUT + echo -e "$FAILED" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "has_failed=true" >> $GITHUB_OUTPUT + else + echo "has_failed=false" >> $GITHUB_OUTPUT + fi + + - name: Comment on PR + if: steps.validate.outputs.has_failed == 'true' + uses: actions/github-script@v7 + with: + script: | + const failedFiles = `${{ steps.validate.outputs.failed }}`.trim().split('\n').filter(f => f); + const body = `### App file validation failed\n\nThe following files could not be converted to JSON:\n\n${failedFiles.map(f => `- \`${f}\``).join('\n')}\n\nPlease check the file format against [\`apps/.template\`](apps/.template) and fix any syntax errors.`; + + // Find and update existing comment or create new one + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(c => c.user.type === 'Bot' && c.body.includes('App file validation failed')); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } From 4304104c59d5ca6765df2abd0135f13d195eadce Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Mon, 7 Sep 2026 14:04:50 +0500 Subject: [PATCH 4/7] app file validation --- .github/workflows/validate-apps.yml | 100 ++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 .github/workflows/validate-apps.yml diff --git a/.github/workflows/validate-apps.yml b/.github/workflows/validate-apps.yml new file mode 100644 index 000000000..fffcbef2a --- /dev/null +++ b/.github/workflows/validate-apps.yml @@ -0,0 +1,100 @@ +name: Validate app files + +on: + pull_request: + paths: + - 'apps/**' + +permissions: + contents: read + pull-requests: write + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Get modified app files + id: changed + run: | + FILES=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }}...HEAD -- 'apps/*' | grep -v '\.template$' || true) + echo "files<> $GITHUB_OUTPUT + echo "$FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Download pla-site-tool + if: steps.changed.outputs.files != '' + run: | + wget -q -O pla-site-tool "$(curl -Ls https://api.github.com/repos/kazam0180/portable-apps/releases | sed 's/[()",{} ]/\n/g' | grep -oi "https.*pla-site-tool.*mage$" | head -1)" + chmod +x pla-site-tool + + - name: Validate files + if: steps.changed.outputs.files != '' + id: validate + run: | + FAILED="" + while IFS= read -r file; do + [ -z "$file" ] && continue + echo "Validating: $file" + set +e + OUTPUT=$(./pla-site-tool -i "$file" 2>&1) + EXIT_CODE=$? + set -e + if [ $EXIT_CODE -ne 0 ]; then + echo "FAILED: $file" + FAILED="${FAILED}${file}\n" + else + rm -f "${file}.json" + fi + done <<< "${{ steps.changed.outputs.files }}" + + if [ -n "$FAILED" ]; then + echo "failed<> $GITHUB_OUTPUT + echo -e "$FAILED" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "has_failed=true" >> $GITHUB_OUTPUT + else + echo "has_failed=false" >> $GITHUB_OUTPUT + fi + + - name: Comment on PR + if: steps.validate.outputs.has_failed == 'true' + uses: actions/github-script@v7 + with: + script: | + const failedFiles = `${{ steps.validate.outputs.failed }}`.trim().split('\n').filter(f => f); + const body = `### App file validation failed\n\nThe following files could not be converted to JSON:\n\n${failedFiles.map(f => `- \`${f}\``).join('\n')}\n\nPlease check the file format against [\`apps/.template\`](apps/.template) and fix any syntax errors.`; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(c => c.user.type === 'Bot' && c.body.includes('App file validation failed')); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } + + - name: Fail if validation errors + if: steps.validate.outputs.has_failed == 'true' + run: | + echo "Validation failed for the files listed in the PR comment." + exit 1 From 6306b9343e7561d10a20980bb5158f4295cec634 Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Mon, 7 Sep 2026 14:04:50 +0500 Subject: [PATCH 5/7] app file validation --- .github/workflows/validate-apps.yml | 101 ++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 .github/workflows/validate-apps.yml diff --git a/.github/workflows/validate-apps.yml b/.github/workflows/validate-apps.yml new file mode 100644 index 000000000..a58622894 --- /dev/null +++ b/.github/workflows/validate-apps.yml @@ -0,0 +1,101 @@ +name: Validate app files + +on: + pull_request: + paths: + - 'apps/**' + +permissions: + contents: read + pull-requests: write + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Get modified app files + id: changed + run: | + FILES=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }}...HEAD -- 'apps/*' | grep -v '\.template$' || true) + echo "files<> $GITHUB_OUTPUT + echo "$FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Download pla-site-tool + if: steps.changed.outputs.files != '' + run: | + wget -q -O pla-site-tool "$(curl -Ls https://api.github.com/repos/kazam0180/portable-apps/releases | sed 's/[()",{} ]/\n/g' | grep -oi "https.*pla-site-tool.*mage$" | head -1)" + chmod +x pla-site-tool + + - name: Validate files + if: steps.changed.outputs.files != '' + id: validate + run: | + FAILED="" + while IFS= read -r file; do + [ -z "$file" ] && continue + echo "Validating: $file" + set +e + OUTPUT=$(./pla-site-tool -i "$file" 2>&1) + EXIT_CODE=$? + set -e + echo "$OUTPUT" + if [ $EXIT_CODE -ne 0 ] || [ ! -s "${file}.json" ] || ! python3 -c "import json; json.load(open('${file}.json'))" 2>/dev/null; then + echo "FAILED: $file" + FAILED="${FAILED}${file}\n" + else + rm -f "${file}.json" + fi + done <<< "${{ steps.changed.outputs.files }}" + + if [ -n "$FAILED" ]; then + echo "failed<> $GITHUB_OUTPUT + echo -e "$FAILED" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "has_failed=true" >> $GITHUB_OUTPUT + else + echo "has_failed=false" >> $GITHUB_OUTPUT + fi + + - name: Comment on PR + if: steps.validate.outputs.has_failed == 'true' + uses: actions/github-script@v7 + with: + script: | + const failedFiles = `${{ steps.validate.outputs.failed }}`.trim().split('\n').filter(f => f); + const body = `### App file validation failed\n\nThe following files could not be converted to JSON:\n\n${failedFiles.map(f => `- \`${f}\``).join('\n')}\n\nPlease check the file format against [\`apps/.template\`](apps/.template) and fix any syntax errors.`; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(c => c.user.type === 'Bot' && c.body.includes('App file validation failed')); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } + + - name: Fail if validation errors + if: steps.validate.outputs.has_failed == 'true' + run: | + echo "Validation failed for the files listed in the PR comment." + exit 1 From de57662a6bcc78d4b05fc0717e50b2c82dc06bb3 Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Mon, 7 Sep 2026 14:26:31 +0500 Subject: [PATCH 6/7] more test --- apps/anotherapp | 1 + apps/anotherotherapp | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 apps/anotherapp create mode 100644 apps/anotherotherapp diff --git a/apps/anotherapp b/apps/anotherapp new file mode 100644 index 000000000..2cd080f66 --- /dev/null +++ b/apps/anotherapp @@ -0,0 +1 @@ +# new app diff --git a/apps/anotherotherapp b/apps/anotherotherapp new file mode 100644 index 000000000..46518acfb --- /dev/null +++ b/apps/anotherotherapp @@ -0,0 +1,13 @@ +# some app + +=== +description of your app. must be within the `===` lines. +only use **bold**, *italic*, ++underline++, lists markdown here +- do not add links or images here +- use SOURCES field if you package someone else's app +=== + +# SCREENSHOTS: https://Portable-Linux-Apps.github.io/contribute_ss.webp +# SITES: https://someapp.io +# SOURCES: https://github.com/name/someapp-appimage +# BUTTONS: Label_of_Button::https://discord.gg Donation_Link::https://someapp.io/donate From 927d69071ce774a63ed7971be57674d0b064c3cd Mon Sep 17 00:00:00 2001 From: Mechamorph Date: Mon, 7 Sep 2026 14:04:50 +0500 Subject: [PATCH 7/7] app file validation --- .github/workflows/validate-apps.yml | 101 ++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 .github/workflows/validate-apps.yml diff --git a/.github/workflows/validate-apps.yml b/.github/workflows/validate-apps.yml new file mode 100644 index 000000000..fb0c7a321 --- /dev/null +++ b/.github/workflows/validate-apps.yml @@ -0,0 +1,101 @@ +name: Validate app files + +on: + pull_request: + paths: + - 'apps/**' + +permissions: + contents: read + pull-requests: write + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Get modified app files + id: changed + run: | + FILES=$(git diff --name-only --diff-filter=ACMR ${{ github.event.pull_request.base.sha }}...HEAD -- 'apps/*' | grep -v '\.template$' || true) + echo "files<> $GITHUB_OUTPUT + echo "$FILES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Download pla-site-tool + if: steps.changed.outputs.files != '' + run: | + wget -q -O pla-site-tool "$(curl -Ls https://api.github.com/repos/kazam0180/portable-apps/releases | sed 's/[()",{} ]/\n/g' | grep -oi "https.*pla-site-tool.*mage$" | head -1)" + chmod +x pla-site-tool + + - name: Validate files + if: steps.changed.outputs.files != '' + id: validate + run: | + FAILED="" + while IFS= read -r file; do + [ -z "$file" ] && continue + echo "Validating: $file" + set +e + OUTPUT=$(./pla-site-tool -i "$file" 2>&1) + EXIT_CODE=$? + set -e + echo "$OUTPUT" + if [ $EXIT_CODE -ne 0 ] || [ ! -s "${file}.json" ] || ! python3 -c "import json; json.load(open('${file}.json'))" 2>/dev/null; then + echo "FAILED: $file" + FAILED="${FAILED}${file}\n" + else + rm -f "${file}.json" + fi + done <<< "${{ steps.changed.outputs.files }}" + + if [ -n "$FAILED" ]; then + echo "failed<> $GITHUB_OUTPUT + echo -e "$FAILED" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "has_failed=true" >> $GITHUB_OUTPUT + else + echo "has_failed=false" >> $GITHUB_OUTPUT + fi + + - name: Comment on PR + if: steps.validate.outputs.has_failed == 'true' + uses: actions/github-script@v7 + with: + script: | + const failedFiles = `${{ steps.validate.outputs.failed }}`.trim().split('\n').filter(f => f); + const body = `### App file validation failed\n\nThe following files could not be converted to JSON:\n\n${failedFiles.map(f => `- \`${f}\``).join('\n')}\n\nPlease check the file format against [\`apps/.template\`](https://github.com/Portable-Linux-Apps/Portable-Linux-Apps.github.io/blob/main/apps/.template) and fix any syntax errors.`; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + const botComment = comments.find(c => c.user.type === 'Bot' && c.body.includes('App file validation failed')); + + if (botComment) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: botComment.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } + + - name: Fail if validation errors + if: steps.validate.outputs.has_failed == 'true' + run: | + echo "Validation failed for the files listed in the PR comment." + exit 1