name: Check Properties Files on PR on: pull_request_target: types: [opened, synchronize, reopened] paths: - "src/main/resources/messages_*.properties" permissions: contents: read # Allow read access to repository content jobs: check-files: if: github.event_name == 'pull_request_target' runs-on: ubuntu-latest permissions: issues: write # Allow posting comments on issues/PRs pull-requests: write steps: - name: Harden Runner uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 with: egress-policy: audit - name: Checkout main branch first uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: "3.x" - name: Get PR data id: get-pr-data uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const prNumber = context.payload.pull_request.number; const repoOwner = context.payload.repository.owner.login; const repoName = context.payload.repository.name; const branch = context.payload.pull_request.head.ref; console.log(`PR Number: ${prNumber}`); console.log(`Repo Owner: ${repoOwner}`); console.log(`Repo Name: ${repoName}`); console.log(`Branch: ${branch}`); core.setOutput("pr_number", prNumber); core.setOutput("repo_owner", repoOwner); core.setOutput("repo_name", repoName); core.setOutput("branch", branch); continue-on-error: true - name: Fetch PR changed files id: fetch-pr-changes env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | echo "Fetching PR changed files..." echo "Getting list of changed files from PR..." gh pr view ${{ steps.get-pr-data.outputs.pr_number }} --json files -q ".files[].path" | grep -E '^src/main/resources/messages_[a-zA-Z_]+\.properties$' > changed_files.txt # Filter only matching property files - name: Determine reference file test id: determine-file uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const fs = require("fs"); const path = require("path"); const prNumber = ${{ steps.get-pr-data.outputs.pr_number }}; const repoOwner = "${{ steps.get-pr-data.outputs.repo_owner }}"; const repoName = "${{ steps.get-pr-data.outputs.repo_name }}"; const prRepoOwner = "${{ github.event.pull_request.head.repo.owner.login }}"; const prRepoName = "${{ github.event.pull_request.head.repo.name }}"; const branch = "${{ steps.get-pr-data.outputs.branch }}"; console.log(`Determining reference file for PR #${prNumber}`); // Validate inputs const validateInput = (input, regex, name) => { if (!regex.test(input)) { throw new Error(`Invalid ${name}: ${input}`); } }; validateInput(repoOwner, /^[a-zA-Z0-9_-]+$/, "repository owner"); validateInput(repoName, /^[a-zA-Z0-9._-]+$/, "repository name"); validateInput(branch, /^[a-zA-Z0-9._/-]+$/, "branch name"); // Get the list of changed files in the PR const { data: files } = await github.rest.pulls.listFiles({ owner: repoOwner, repo: repoName, pull_number: prNumber, }); // Filter for relevant files based on the PR changes const changedFiles = files .map(file => file.filename) .filter(file => /^src\/main\/resources\/messages_[a-zA-Z_]+\.properties$/.test(file)); console.log("Changed files:", changedFiles); // Create a temporary directory for PR files const tempDir = "pr-branch"; if (!fs.existsSync(tempDir)) { fs.mkdirSync(tempDir, { recursive: true }); } // Download and save each changed file for (const file of changedFiles) { const { data: fileContent } = await github.rest.repos.getContent({ owner: prRepoOwner, repo: prRepoName, path: file, ref: branch, }); const content = Buffer.from(fileContent.content, "base64").toString("utf-8"); const filePath = path.join(tempDir, file); const dirPath = path.dirname(filePath); if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); } fs.writeFileSync(filePath, content); console.log(`Saved file: ${filePath}`); } // Output the list of changed files for further processing const fileList = changedFiles.join(" "); core.exportVariable("FILES_LIST", fileList); console.log("Files saved and listed in FILES_LIST."); // Determine reference file let referenceFilePath; if (changedFiles.includes("src/main/resources/messages_en_GB.properties")) { console.log("Using PR branch reference file."); const { data: fileContent } = await github.rest.repos.getContent({ owner: prRepoOwner, repo: prRepoName, path: "src/main/resources/messages_en_GB.properties", ref: branch, }); referenceFilePath = "pr-branch-messages_en_GB.properties"; const content = Buffer.from(fileContent.content, "base64").toString("utf-8"); fs.writeFileSync(referenceFilePath, content); } else { console.log("Using main branch reference file."); const { data: fileContent } = await github.rest.repos.getContent({ owner: repoOwner, repo: repoName, path: "src/main/resources/messages_en_GB.properties", ref: "main", }); referenceFilePath = "main-branch-messages_en_GB.properties"; const content = Buffer.from(fileContent.content, "base64").toString("utf-8"); fs.writeFileSync(referenceFilePath, content); } console.log(`Reference file path: ${referenceFilePath}`); core.exportVariable("REFERENCE_FILE", referenceFilePath); - name: Run Python script to check files id: run-check run: | echo "Running Python script to check files..." python .github/scripts/check_language_properties.py \ --actor ${{ github.event.pull_request.user.login }} \ --reference-file "${REFERENCE_FILE}" \ --branch "pr-branch" \ --files "${FILES_LIST[@]}" > result.txt continue-on-error: true # Continue the job even if this step fails - name: Capture output id: capture-output run: | if [ -f result.txt ] && [ -s result.txt ]; then echo "Test, capturing output..." SCRIPT_OUTPUT=$(cat result.txt) echo "SCRIPT_OUTPUT<> $GITHUB_ENV echo "$SCRIPT_OUTPUT" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV echo "${SCRIPT_OUTPUT}" # Determine job failure based on script output if [[ "$SCRIPT_OUTPUT" == *"❌"* ]]; then echo "FAIL_JOB=true" >> $GITHUB_ENV else echo "FAIL_JOB=false" >> $GITHUB_ENV fi else echo "No update found." echo "SCRIPT_OUTPUT=" >> $GITHUB_ENV echo "FAIL_JOB=false" >> $GITHUB_ENV fi - name: Post comment on PR if: env.SCRIPT_OUTPUT != '' uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const { GITHUB_REPOSITORY, SCRIPT_OUTPUT } = process.env; const [repoOwner, repoName] = GITHUB_REPOSITORY.split('/'); const issueNumber = context.issue.number; // Find existing comment const comments = await github.rest.issues.listComments({ owner: repoOwner, repo: repoName, issue_number: issueNumber }); const comment = comments.data.find(c => c.body.includes("## 🚀 Translation Verification Summary")); // Only update or create comments by the action user const expectedActor = "github-actions[bot]"; if (comment && comment.user.login === expectedActor) { // Update existing comment await github.rest.issues.updateComment({ owner: repoOwner, repo: repoName, comment_id: comment.id, body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n` }); console.log("Updated existing comment."); } else if (!comment) { // Create new comment if no existing comment is found await github.rest.issues.createComment({ owner: repoOwner, repo: repoName, issue_number: issueNumber, body: `## 🚀 Translation Verification Summary\n\n\n${SCRIPT_OUTPUT}\n` }); console.log("Created new comment."); } else { console.log("Comment update attempt denied. Actor does not match."); } - name: Fail job if errors found if: env.FAIL_JOB == 'true' run: | echo "Failing the job because errors were detected." exit 1