on: workflow_call: inputs: version_file: required: false type: string nightly_manual_trigger: required: false type: boolean outputs: version: description: "version" value: ${{ jobs.get-environment.outputs.version }} release: description: "release" value: ${{ jobs.get-environment.outputs.release }} stability: description: "branch stability (stable, testing, unstable, canary)" value: ${{ jobs.get-environment.outputs.stability }} target_stability: description: "Final target branch stability (stable, testing, unstable, canary or not defined if not a pull request)" value: ${{ jobs.get-environment.outputs.target_stability }} is_nightly: description: "if the current workflow run is considered a nightly" value: ${{ jobs.get-environment.outputs.is_nightly }} release_type: description: "type of release (hotfix, release or not defined if not a release)" value: ${{ jobs.get-environment.outputs.release_type }} is_targeting_feature_branch: description: "if it is a PR, check if targeting a feature branch" value: ${{ jobs.get-environment.outputs.is_targeting_feature_branch }} skip_workflow: description: "if the current workflow should be skipped" value: ${{ jobs.get-environment.outputs.skip_workflow }} labels: description: "list of labels on the PR" value: ${{ jobs.get-environment.outputs.labels }} jobs: get-environment: runs-on: ubuntu-24.04 outputs: version: ${{ steps.get_version.outputs.version }} release: ${{ steps.get_release.outputs.release }} stability: ${{ steps.get_stability.outputs.stability }} target_stability: ${{ steps.get_stability.outputs.target_stability }} release_type: ${{ steps.get_release_type.outputs.release_type }} is_targeting_feature_branch: ${{ steps.get_stability.outputs.is_targeting_feature_branch }} is_nightly: ${{ steps.get_nightly_status.outputs.is_nightly }} skip_workflow: ${{ steps.skip_workflow.outputs.result }} labels: ${{ steps.has_skip_label.outputs.labels }} steps: - name: Check if PR has skip label id: has_skip_label uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | let hasSkipLabel = false; let labels = []; if (${{ contains(fromJSON('["pull_request"]') , github.event_name) }} === true) { try { const fetchedLabels = await github.rest.issues.listLabelsOnIssue({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number }); fetchedLabels.data.forEach(({ name }) => { labels.push(name); if (name === '${{ format('skip-workflow-{0}', github.workflow) }}') { hasSkipLabel = true; } }); } catch (e) { core.warning(`failed to list labels: ${e}`); } } core.setOutput('labels', labels); return hasSkipLabel; - name: Checkout sources (current branch) uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 with: fetch-depth: ${{ steps.has_skip_label.outputs.result == 'true' && 100 || 1 }} - if: ${{ steps.has_skip_label.outputs.result == 'true' }} name: Get workflow triggered paths id: get_workflow_triggered_paths uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const fs = require('fs'); let paths = []; const workflowFilePath = '${{ github.workflow_ref }}'.replace('${{ github.repository }}/', '').split('@').shift(); if (fs.existsSync(workflowFilePath)) { const workflowFileContent = fs.readFileSync(workflowFilePath, 'utf8'); const workflowFileContentLines = workflowFileContent.split('\n'); let hasReadOn = false; let hasReadPullRequest = false; let hasReadPaths = false; for (const line of workflowFileContentLines) { if (line.match(/^on:\s*$/)) { hasReadOn = true; continue; } if (line.match(/^\s{2}pull_request(_target)?:\s*$/)) { hasReadPullRequest = true; continue; } if (line.match(/^\s{4}paths:\s*$/)) { hasReadPaths = true; continue; } if (hasReadOn && hasReadPullRequest && hasReadPaths) { const matches = line.match(/^\s{6}-\s['"](.+)['"]\s*$/); if (matches) { paths.push(matches[1].trim()); } else { break; } } } } if (paths.length === 0) { paths = ['**']; } console.log(paths); return paths; - if: ${{ steps.has_skip_label.outputs.result == 'true' }} name: Get push changes id: get_push_changes uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5 with: since_last_remote_commit: true json: true escape_json: false files: ${{ join(fromJSON(steps.get_workflow_triggered_paths.outputs.result), ';') }} files_separator: ';' - name: Check if current workflow should be skipped id: skip_workflow uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | if (${{ steps.has_skip_label.outputs.result }} === false) { return false; } const label = '${{ format('skip-workflow-{0}', github.workflow) }}'; if ('${{ steps.get_push_changes.outputs.any_changed }}' === 'true') { try { await github.rest.issues.removeLabel({ name: label, owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number }); core.notice(`label ${label} removed because changes were detected on last push.`); } catch (e) { core.warning(`failed to remove label ${label}: ${e}`); } return false; } return true; - if: ${{ github.event_name == 'pull_request' }} name: Get nested pull request path id: pr_path uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const prPath = ['${{ github.head_ref }}', '${{ github.base_ref }}']; const result = await github.rest.pulls.list({ owner: context.repo.owner, repo: context.repo.repo, per_page: 100, state: 'open' }); let found = true; while (found) { found = false; result.data.forEach(({ head: { ref: headRef }, base: { ref: baseRef} }) => { if (headRef === prPath[prPath.length - 1] && ! prPath.includes(baseRef)) { found = true; prPath.push(baseRef); } }); } return prPath; - name: Get stability id: get_stability uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const getStability = (branchName) => { switch (true) { case /(^develop$)|(^dev-\d{2}\.\d{2}\.x$)/.test(branchName): return 'unstable'; case /(^release.+)|(^hotfix.+)/.test(branchName): return 'testing'; case /(^master$)|(^\d{2}\.\d{2}\.x$)/.test(branchName): return 'stable'; default: return 'canary'; } }; core.setOutput('stability', getStability('${{ github.head_ref || github.ref_name }}')); let isTargetingFeatureBranch = false; if ("${{ github.event_name }}" === "pull_request") { let targetStability = 'canary'; const prPath = ${{ steps.pr_path.outputs.result || '[]' }}; prPath.shift(); // remove current branch if (prPath.length && getStability(prPath[0]) === 'canary') { isTargetingFeatureBranch = true; } prPath.every((branchName) => { console.log(`checking stability of ${branchName}`) targetStability = getStability(branchName); if (targetStability !== 'canary') { return false; } return true; }); core.setOutput('target_stability', targetStability); } core.setOutput('is_targeting_feature_branch', isTargetingFeatureBranch); - name: Detect nightly status uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 id: get_nightly_status env: NIGHTLY_MANUAL_TRIGGER: ${{ inputs.nightly_manual_trigger }} with: script: | const getNightlyInput = () => { const nightly_manual_trigger = process.env.NIGHTLY_MANUAL_TRIGGER; console.log(nightly_manual_trigger); if (typeof nightly_manual_trigger === 'undefined' || nightly_manual_trigger === '' || '${{ github.repository }}'.match(/^workflow-.*$/)) { return 'false'; } else if (context.eventName === 'schedule' || context.eventName === 'workflow_dispatch' && nightly_manual_trigger === 'true' ) { return 'true'; } return 'false'; }; core.setOutput('is_nightly', getNightlyInput()); - name: Get version id: get_version uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const { execSync } = require('child_process'); const fs = require('fs'); let version = ''; if ('${{ inputs.version_file }}'.match(/pom\.xml$/)) { version = execSync(`grep -m 1 ".*" ${{ inputs.version_file }} | sed 's/.*\\(.*\\)<\\/version>.*/\\1/'`).toString().trim(); } else if ('${{ steps.get_stability.outputs.stability }}' === 'stable') { const { owner, repo } = context.repo; // Fetch the most recent tag for plugins const { data: tags } = await github.rest.repos.listTags({ owner, repo, per_page: 10 }); let latestTag = null; let latestDate = 0; // Filter tags matching format plugins-YYYYMMDD for (const tag of tags) { const match = tag.name.match(/^plugins-(\d{8})$/); const tagDate = parseInt(match[1], 10); // ensure we get the true latest tag and not the most recent created if (tagDate > latestDate) { latestTag = tag.name; latestDate = tagDate; } } console.log(`Most recent tag found: ${latestTag}`) // Get current release tag from .version file version = fs.readFileSync('.version.plugins', 'utf8').trim(); console.log(`Stable version based on .version.plugins file will be: ${version}`) } else if ('${{ steps.get_stability.outputs.stability }}' === 'testing') { const branchName = "${{ github.head_ref || github.ref_name }}"; const matches = branchName.match(/^(?:release|hotfix)-(\d{8})$/); if (matches) { version = matches[1]; } else { throw new Error('invalid version'); } } else if ('${{ steps.get_stability.outputs.stability }}' === 'unstable') { const currentDate = new Date(); version = `${currentDate.getFullYear()}${("0" + (currentDate.getMonth() + 1)).slice(-2)}${String(currentDate.getDate()).padStart(2, '0')}`; } else { const currentDate = new Date(); version = `${currentDate.getFullYear()}${("0" + (currentDate.getMonth() + 1)).slice(-2)}00`; } core.setOutput('version', version); - name: "Get release: 1 for testing / stable, for others" id: get_release uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | let release = ''; if (${{ contains(fromJSON('["testing", "unstable"]') , steps.get_stability.outputs.stability) }} === true) { release = "1" } else { release = Date.now() } core.setOutput('release', release); - name: "Get release type: hotfix, release or not defined if not a release" id: get_release_type run: | RELEASE_TYPE=$(echo "${{ github.head_ref || github.ref_name }}" | cut -d '-' -f 1) if [[ "$RELEASE_TYPE" == "hotfix" || "$RELEASE_TYPE" == "release" ]]; then echo "release_type=$RELEASE_TYPE" >> $GITHUB_OUTPUT fi shell: bash - name: Display info in job summary uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const outputTable = [ [{data: 'Name', header: true}, {data: 'Value', header: true}], ['version', '${{ steps.get_version.outputs.version }}'], ['release', '${{ steps.get_release.outputs.release }}'], ['stability', '${{ steps.get_stability.outputs.stability }}'], ['release_type', '${{ steps.get_release_type.outputs.release_type || 'not defined because this is not a release' }}'], ['is_targeting_feature_branch', '${{ steps.get_stability.outputs.is_targeting_feature_branch }}'], ['target_stability', '${{ steps.get_stability.outputs.target_stability || 'not defined because current run is not triggered by pull request event' }}'], ['is_nightly', '${{ steps.get_nightly_status.outputs.is_nightly }}'], ['skip_workflow', '${{ steps.skip_workflow.outputs.result }}'], ['labels', '${{ steps.has_skip_label.outputs.labels }}'], ]; core.summary .addHeading(`${context.workflow} environment outputs`) .addTable(outputTable); if ("${{ github.event_name }}" === "pull_request") { const prPath = ${{ steps.pr_path.outputs.result || '[]' }}; const mainBranchName = prPath.pop(); let codeBlock = ` %%{ init: { 'gitGraph': { 'mainBranchName': '${mainBranchName}', 'showCommitLabel': false } } }%% gitGraph commit`; prPath.reverse().forEach((branchName) => { codeBlock = `${codeBlock} branch ${branchName} checkout ${branchName} commit`; }); core.summary .addHeading('Git workflow') .addCodeBlock( codeBlock, "mermaid" ); } core.summary.write();