diff --git a/.github/actions/check-pr-semver-labels/action.yml b/.github/actions/check-pr-semver-labels/action.yml new file mode 100644 index 0000000..abfbe10 --- /dev/null +++ b/.github/actions/check-pr-semver-labels/action.yml @@ -0,0 +1,136 @@ +name: Check PR semantic versioning labels +description: "Check semantic versioning labels" + +inputs: + github_token: + required: true + description: The github token used to read PR metadata + +outputs: + semver_app: + description: "Application semantic version label" + value: ${{ steps.check_labels.outputs.semver_app }} + semver_chart: + description: "Chart semantic version label" + value: ${{ steps.check_labels.outputs.semver_chart }} + skip_release: + description: "Skip release flag" + value: ${{ steps.check_labels.outputs.skip_release }} +runs: + using: "composite" + steps: + - name: Retrieve semantic versioning labels on PR + id: check_labels + uses: actions/github-script@v6.3.3 + env: + VALID_APP_VERSION_LABELS: patch, minor, major, ignore-for-release + VALID_CHART_VERSION_LABELS: chart-patch, chart-minor, chart-major + SKIP_RELEASE_LABEL: skip-release + with: + github-token: ${{ inputs.github_token }} + script: | + const { VALID_APP_VERSION_LABELS, VALID_CHART_VERSION_LABELS, SKIP_RELEASE_LABEL } = process.env + const validLabelsSemver = VALID_APP_VERSION_LABELS.trim().split(',').map(word => word.trim()).filter(word => word.length > 0); + const validLabelsChartSemver = VALID_CHART_VERSION_LABELS.trim().split(',').map(word => word.trim()).filter(word => word.length > 0); + const skipReleaseLabel = SKIP_RELEASE_LABEL.trim(); + var issueNumber = context.issue.number; + if (issueNumber === undefined) { + core.info('Retrieving issue number from merge commit message') + var mergeCommit = await github.rest.git.getCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + commit_sha: context.sha, + }); + if (!mergeCommit) { + throw `Could not find commit for sha ${context.sha}`; + } + const commitMessage = mergeCommit.data.message; + core.info(`Commit message: ${commitMessage}`); + const extractedPrNumber = commitMessage.match(/#(\d{1,9})/); + if(extractedPrNumber ==null || extractedPrNumber.length <1){ + throw `Could not extract PR number from commit message ${commitMessage}`; + } + const prNumber = extractedPrNumber[1]; + core.info(`Extracted PR number: ${prNumber}`); + issueNumber = prNumber; + } + core.info(`Issue number: ${issueNumber}`); + var labelsQuery = await github.rest.issues.listLabelsOnIssue({ + issue_number: issueNumber, + owner: context.repo.owner, + repo: context.repo.repo + }); + var prLabels = labelsQuery.data.map(label => label.name); + core.info( + `PR labels: ${prLabels} + Valid labels semver: ${validLabelsSemver} + Valid labels chart semver: ${validLabelsChartSemver} + Skip release label: ${skipReleaseLabel}` + ); + var matchedLabelsSemver = []; + var matchedLabelsChartSemver = []; + var skipRelease = false; + prLabels.forEach(label => { + core.info(`Analyzing label: ${label}`); + if (validLabelsSemver.includes(label)) { + matchedLabelsSemver.push(label) + } + if (validLabelsChartSemver.includes(label)) { + matchedLabelsChartSemver.push(label) + } + if (label == skipReleaseLabel) { + skipRelease = true; + } + } + ); + core.info( + `Matched labels semver: ${matchedLabelsSemver} + chart semver: ${matchedLabelsChartSemver}` + ) + var comments = await github.rest.issues.listComments({ + issue_number: issueNumber, + owner: context.repo.owner, + repo: context.repo.repo + }); + for (const comment of comments.data) { + if (comment.body.includes('Pull request label validation error!')) { + github.rest.issues.deleteComment({ + issue_number: issueNumber, + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: comment.id + }) + } + } + + if ((matchedLabelsSemver.length != 1 || matchedLabelsChartSemver.length != 1) && !skipRelease) { + const errorMessage = + `Pull request label validation error! + Required label for app version: ${validLabelsSemver.map(label => `\`${label}\``).join()} + Required label for chart version: ${validLabelsChartSemver.map(label => `\`${label}\``).join()}. + + Found labels on PR... + + app version: ${matchedLabelsSemver.map(label => `\`${label}\``).join()} + chart version: ${matchedLabelsChartSemver.map(label => `\`${label}\``).join()} + + If you want to skip release for this PR add \`${skipReleaseLabel}\` label + ` + github.rest.issues.createComment({ + issue_number: issueNumber, + owner: context.repo.owner, + repo: context.repo.repo, + body: errorMessage + }) + core.setFailed('Missing required labels') + } else { + if (skipRelease) { + core.setOutput("semver_app", "none"); + core.setOutput("semver_chart", "patch"); + core.setOutput("skip_release", "True"); + } else { + core.setOutput("semver_app", matchedLabelsSemver[0]); + core.setOutput("semver_chart", matchedLabelsChartSemver[0]); + core.setOutput("skip_release", "False"); + } + } \ No newline at end of file diff --git a/.github/workflows/check_pr.yml b/.github/workflows/check_pr.yml new file mode 100644 index 0000000..41e8e06 --- /dev/null +++ b/.github/workflows/check_pr.yml @@ -0,0 +1,33 @@ +name: Check PR + +# Controls when the workflow will run +on: + pull_request: + branches: + - main + types: [ opened, synchronize, labeled, unlabeled, reopened, edited ] + + +permissions: + pull-requests: write + +jobs: + check_labels: + name: Check Required Labels + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - uses: ./.github/actions/check-pr-semver-labels + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + check_size: + runs-on: ubuntu-latest + name: Check Size + steps: + + - name: Check PR Size + uses: pagopa/github-actions-template/check-pr-size@3fae741d94bcb9873f2447e95cc4ddea6f77be4d + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + ignored_files: 'src/test/' \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..ae05e67 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,85 @@ +name: Trigger pipeline deploy + +on: + push: + branches: [ "main" ] + + workflow_dispatch: + +permissions: + pull-requests: write + +jobs: + semver_labels: + name: Read semver labels from merged PR + runs-on: ubuntu-latest + outputs: + semver_app: ${{ steps.semver_labels.outputs.semver_app }} + semver_chart: ${{ steps.semver_labels.outputs.semver_chart }} + skip_release: ${{ steps.semver_labels.outputs.skip_release }} + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.github_token }} + - uses: ./.github/actions/check-pr-semver-labels + id: semver_labels + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + azure-devops-deploy-pipeline: + name: Trigger azure devops deploy pipeline + runs-on: ubuntu-latest + needs: semver_labels + environment: prod + steps: + - name: Configure pipeline parameters + id: configure_pipeline_parameters + run: | + echo "📝 Inputs" + echo "pull request merged: ${{ github.event.pull_request.merged}}" + echo "Semver app: ${{ needs.semver_labels.outputs.semver_app}}" + echo "Semver chart: ${{ needs.semver_labels.outputs.semver_chart}}" + echo "Skip release: ${{ needs.semver_labels.outputs.skip_release}}" + semverApp=$(echo ${{ needs.semver_labels.outputs.semver_app}} | sed "s/ignore-for-release/none/") + semverChart=$(echo ${{ needs.semver_labels.outputs.semver_chart }} | cut -d'-' -f2 ) + skipRelease=$(echo ${{ needs.semver_labels.outputs.skip_release }}) + echo "DEV_DEPLOY=True" >> $GITHUB_OUTPUT + echo "UAT_PROD_DEPLOY=True" >> $GITHUB_OUTPUT + echo "RELEASE_SEMVER=$semverApp" >> $GITHUB_OUTPUT + echo "RELEASE_CHART_SEMVER=$semverChart" >> $GITHUB_OUTPUT + echo "FORCE_REPLACE_DOCKER_IMAGE=False" >> $GITHUB_OUTPUT + echo "SKIP_RELEASE=$skipRelease" >> $GITHUB_OUTPUT + echo "UAT_SKIP_BLUE_DEPLOYMENT=True" >> $GITHUB_OUTPUT + echo "PROD_SKIP_BLUE_DEPLOYMENT=True" >> $GITHUB_OUTPUT + echo "SKIP_BUILD=False" >> $GITHUB_OUTPUT + shell: bash + - name: Log pipeline parameters + run: | + echo "🪛 Pipeline parameters" + echo "DEV_DEPLOY=${{ steps.configure_pipeline_parameters.outputs.DEV_DEPLOY }}" + echo "UAT_PROD_DEPLOY=${{ steps.configure_pipeline_parameters.outputs.UAT_PROD_DEPLOY }}" + echo "RELEASE_SEMVER=${{ steps.configure_pipeline_parameters.outputs.RELEASE_SEMVER }}" + echo "RELEASE_CHART_SEMVER=${{ steps.configure_pipeline_parameters.outputs.RELEASE_CHART_SEMVER }}" + echo "FORCE_REPLACE_DOCKER_IMAGE=${{ steps.configure_pipeline_parameters.outputs.FORCE_REPLACE_DOCKER_IMAGE }}" + echo "SKIP_RELEASE=${{ steps.configure_pipeline_parameters.outputs.SKIP_RELEASE }}" + echo "UAT_SKIP_BLUE_DEPLOYMENT=${{ steps.configure_pipeline_parameters.outputs.UAT_SKIP_BLUE_DEPLOYMENT }}" + echo "PROD_SKIP_BLUE_DEPLOYMENT=${{ steps.configure_pipeline_parameters.outputs.PROD_SKIP_BLUE_DEPLOYMENT }}" + echo "SKIP_BUILD=${{ steps.configure_pipeline_parameters.outputs.SKIP_BUILD }}" + shell: bash + - name: Azure Pipelines Action + uses: jacopocarlini/azure-pipelines@v1.3 + with: + azure-devops-project-url: https://dev.azure.com/pagopaspa/pagoPA-projects + azure-pipeline-name: 'pagopa-ecommerce-helpdesk-service.deploy' + azure-devops-token: ${{ secrets.AZURE_DEVOPS_TOKEN }} + azure-template-parameters: | + { + "DEV_DEPLOY": "${{ steps.configure_pipeline_parameters.outputs.DEV_DEPLOY }}", + "UAT_PROD_DEPLOY": "${{ steps.configure_pipeline_parameters.outputs.UAT_PROD_DEPLOY }}", + "SKIP_BUILD": "${{ steps.configure_pipeline_parameters.outputs.SKIP_BUILD }}", + "RELEASE_SEMVER": "${{ steps.configure_pipeline_parameters.outputs.RELEASE_SEMVER }}", + "RELEASE_CHART_SEMVER": "${{ steps.configure_pipeline_parameters.outputs.RELEASE_CHART_SEMVER }}", + "FORCE_REPLACE_DOCKER_IMAGE": "${{ steps.configure_pipeline_parameters.outputs.FORCE_REPLACE_DOCKER_IMAGE }}", + "SKIP_RELEASE": "${{ steps.configure_pipeline_parameters.outputs.SKIP_RELEASE }}", + "UAT_SKIP_BLUE_DEPLOYMENT": "${{ steps.configure_pipeline_parameters.outputs.UAT_SKIP_BLUE_DEPLOYMENT }}", + "PROD_SKIP_BLUE_DEPLOYMENT": "${{ steps.configure_pipeline_parameters.outputs.PROD_SKIP_BLUE_DEPLOYMENT }}" + } \ No newline at end of file