diff --git a/.cruft.json b/.cruft.json index cb86994..1635917 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,10 +1,11 @@ { "template": "https://github.com/vshn/appcat-cookiecutter", - "commit": "128d270ba199a581122ff74c2f82b8e3019490f0", + "commit": "69411951bb5ca1ff063313896b252d274ec50c16", "checkout": null, "context": { "cookiecutter": { "app_name": "provider-exoscale", + "appcat_repo": "vshn/appcat", "component_repo": "vshn/component-appcat", "push_upbound": true, "push_package": true, @@ -13,7 +14,8 @@ ".github/workflows/cruft-update.yml", ".github/changelog-configuration.json" ], - "_template": "https://github.com/vshn/appcat-cookiecutter" + "_template": "https://github.com/vshn/appcat-cookiecutter", + "_commit": "69411951bb5ca1ff063313896b252d274ec50c16" } }, "directory": null diff --git a/.github/changelog-configuration.json b/.github/changelog-configuration.json index 02c9648..f5848c5 100644 --- a/.github/changelog-configuration.json +++ b/.github/changelog-configuration.json @@ -32,5 +32,8 @@ ] } ], - "template": "${{CATEGORIZED_COUNT}} changes since ${{FROM_TAG}}\n\n${{CHANGELOG}}" + "template": "${{CATEGORIZED_COUNT}} changes since ${{FROM_TAG}}\n\n${{CHANGELOG}}", + "ignore_labels": [ + "ignoreChangelog" + ] } diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..bacb03c --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,43 @@ +name: Check + +on: + schedule: + - cron: '0 7 * * *' # Every day at 07:00 UTC + workflow_dispatch: + +jobs: + check-develop-ahead: + runs-on: ubuntu-latest + steps: + - name: Checkout full history + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check if develop contains all commits from master + run: | + if [ -n "$(git rev-list origin/master ^origin/develop)" ]; then + echo "❌ develop is missing commits from master:" + git log origin/develop..origin/master --oneline + exit 1 + else + echo "βœ… develop includes all commits from master" + fi + + - name: Notify Rocket.Chat if develop is behind + if: failure() + run: | + curl -X POST "$ROCKETCHAT_WEBHOOK_URL" \ + -H 'Content-Type: application/json' \ + -d "{ + \"text\": \"🚨 *develop is behind master* in \`${{ github.repository }}\`\nπŸ” Please merge \`master\` into \`develop\` to stay in sync.\", + \"attachments\": [{ + \"title\": \"GitHub Workflow: ${{ github.workflow }}\", + \"title_link\": \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\", + \"text\": \"Triggered by: ${{ github.actor }}\", + \"color\": \"#ff0000\" + }] + }" + env: + ROCKETCHAT_WEBHOOK_URL: ${{ secrets.ROCKETCHAT_WEBHOOK_URL }} + diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml new file mode 100644 index 0000000..d7ddaa1 --- /dev/null +++ b/.github/workflows/merge.yml @@ -0,0 +1,388 @@ +name: Merge + +on: + issue_comment: + types: [created] + + +env: + APP_NAME: provider-exoscale + COMPONENT_REPO: vshn/component-appcat + APPCAT_REPO: vshn/appcat + +jobs: + check-conditions: + if: github.event.issue.pull_request && github.event.comment.body == '/merge' + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }} + outputs: + #appcat branch vars + is-hotfix: ${{ steps.appcat.outputs.is-hotfix }} + base-branch: ${{ steps.appcat.outputs.base-branch }} + feature-branch: ${{ steps.appcat.outputs.feature-branch }} + last-sha: ${{ steps.appcat.outputs.last-sha }} + pr-number: ${{ steps.appcat.outputs.pr-number }} + mergeable: ${{ steps.appcat.outputs.mergeable }} + approved: ${{ steps.appcat.outputs.approved }} + author: ${{ steps.appcat.outputs.author }} + + #component branch vars + comp-pr-number: ${{ steps.component.outputs.pr-number }} + comp-url: ${{ steps.component.outputs.url }} + comp-feature-branch: ${{ steps.component.outputs.feature-branch }} + comp-last-sha: ${{ steps.component.outputs.last-sha }} + comp-mergeable: ${{ steps.component.outputs.mergeable }} + comp-approved: ${{ steps.component.outputs.approved }} + steps: + - name: Comment with workflow run link + run: | + gh api repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments \ + -f body="πŸ”— [Workflow run triggered by this comment](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get appcat PR metadata + id: appcat + run: | + PR_NUMBER=${{ github.event.issue.number }} + PR_DATA=$(gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json title,labels,baseRefName,headRefName,headRefOid,mergeable,reviews,author) + + TITLE=$(echo "$PR_DATA" | jq -r '.title') + LABELS=$(echo "$PR_DATA" | jq -r '[.labels[].name]') + BASE=$(echo "$PR_DATA" | jq -r '.baseRefName') + BRANCH=$(echo "$PR_DATA" | jq -r '.headRefName') + SHA=$(echo "$PR_DATA" | jq -r '.headRefOid') + MERGEABLE=$(echo "$PR_DATA" | jq -r '.mergeable') + APPROVED=$(echo "$PR_DATA" | jq -r '.reviews // [] | map(select(.state == "APPROVED")) | length') + AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login') + + # Check for hotfix in title or labels + if echo "$TITLE" | grep -i 'hotfix' > /dev/null || echo "$LABELS" | grep -i 'hotfix' > /dev/null; then + echo "is-hotfix=true" >> "$GITHUB_OUTPUT" + else + echo "is-hotfix=false" >> "$GITHUB_OUTPUT" + fi + + echo "base-branch=$BASE" >> "$GITHUB_OUTPUT" + echo "feature-branch=$BRANCH" >> "$GITHUB_OUTPUT" + echo "last-sha=$SHA" >> "$GITHUB_OUTPUT" + echo "pr-number=$PR_NUMBER" >> "$GITHUB_OUTPUT" + echo "mergeable=$MERGEABLE" >> "$GITHUB_OUTPUT" + echo "approved=$APPROVED" >> "$GITHUB_OUTPUT" + echo "author=$AUTHOR" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check approvals on appcat PR + run: | + APPROVED="${{ steps.appcat.outputs.approved }}" + if [ "$APPROVED" -eq 0 ]; then + echo "❌ PR in appcat is not approved" + exit 1 + fi + + - name: Check required status checks on appcat PR + run: | + PASSED=$(gh api repos/${{ github.repository }}/commits/${{ steps.appcat.outputs.last-sha }}/check-runs \ + --jq '[.check_runs[].conclusion] | all(. == "success" or . == "skipped")') + + if [ "$PASSED" != "true" ]; then + echo "❌ Required status checks did not pass" + exit 1 + fi + + - name: Check for merge conflicts on appcat PR + id: check-conflicts + run: | + MERGEABLE="${{ steps.appcat.outputs.mergeable }}" + if [ "$MERGEABLE" != "MERGEABLE" ]; then + echo "❌ Pull request has merge conflicts!" + exit 1 + fi + echo "βœ… Pull request is mergeable" + + - name: Get component PR metadata + id: component + run: | + COMPONENT_BRANCH="appcat/${{ steps.appcat.outputs.pr-number }}/${{ steps.appcat.outputs.feature-branch }}" + PR_DATA=$(gh pr list --repo "$COMPONENT_REPO" --head "$COMPONENT_BRANCH" --state open --json number,headRefName,mergeable,url,reviews,headRefOid) + PR_NUMBER=$(echo "$PR_DATA" | jq -r '.[0].number') + PR_URL=$(echo "$PR_DATA" | jq -r '.[0].url') + SHA=$(echo "$PR_DATA" | jq -r '.[0].headRefOid') + MERGEABLE=$(echo "$PR_DATA" | jq -r '.[0].mergeable') + APPROVED=$(echo "$PR_DATA" | jq -r '.[0].reviews // [] | map(select(.state == "APPROVED")) | length') + + echo "pr-number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "url=$PR_URL" >> $GITHUB_OUTPUT + echo "feature-branch=$COMPONENT_BRANCH" >> $GITHUB_OUTPUT + echo "last-sha=$SHA" >> $GITHUB_OUTPUT + echo "mergeable=$MERGEABLE" >> $GITHUB_OUTPUT + echo "approved=$APPROVED" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if component PR found + run: | + PR_NUMBER="${{ steps.component.outputs.pr-number }}" + if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" == "null" ]; then + echo "❌ No matching PR in component repo for branch $COMPONENT_BRANCH" + exit 1 + fi + + - name: Check required status checks on appcat PR + run: | + PASSED=$(gh api repos/$COMPONENT_REPO/commits/${{ steps.component.outputs.last-sha }}/check-runs \ + --jq '[.check_runs[].conclusion] | all(. == "success" or . == "skipped")') + + if [ "$PASSED" != "true" ]; then + echo "❌ Required status checks did not pass" + exit 1 + fi + + - name: Check for merge conflicts on component PR + run: | + MERGEABLE="${{ steps.component.outputs.mergeable }}" + if [ "$MERGEABLE" != "MERGEABLE" ]; then + echo "❌ PR in component is not mergeable: ${{ steps.component.outputs.url }}" + exit 1 + fi + + - name: Check if component PR is approved + run: | + APPROVED="${{ steps.component.outputs.approved }}" + if [ "$APPROVED" -eq 0 ]; then + echo "❌ Component PR not approved: ${{ steps.component.outputs.url }}" + exit 1 + fi + + merge-feature: + needs: check-conditions + if: needs.check-conditions.outputs.base-branch == 'develop' && needs.check-conditions.outputs.is-hotfix == 'false' + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }} + steps: + - name: Clone and patch component repo + run: | + COMPONENT_BRANCH="${{ needs.check-conditions.outputs.comp-feature-branch }}" + APPCAT_FR_COMMIT_SHA="${{ needs.check-conditions.outputs.last-sha }}" + echo "πŸ”§ Using commit: $APPCAT_FR_COMMIT_SHA" + echo "πŸ”§ Cloning branch: $COMPONENT_BRANCH" + + git clone https://x-access-token:$GH_TOKEN@github.com/"$COMPONENT_REPO".git + cd component-appcat + git checkout "$COMPONENT_BRANCH" + + echo "πŸ”§ Patching class/defaults.yml..." + yq e ".parameters.appcat.images.appcat.tag = \"$APPCAT_FR_COMMIT_SHA\"" class/defaults.yml \ + | diff -B class/defaults.yml - \ + | patch class/defaults.yml - || echo "βœ… No patch needed" + + make gen-golden-all + + git config --global user.email "githubbot@vshn.ch" + git config --global user.name "GitHubBot" + + git add . + if git diff --cached --quiet; then + echo "βœ… No changes to commit" + else + git commit -m "Auto update from appcat PR #${{ needs.check-conditions.outputs.pr-number }}, dependency $APPCAT_FR_COMMIT_SHA" + git push origin "$COMPONENT_BRANCH" + fi + + - name: Retry mergeable check + run: | + for i in {1..5}; do + MERGEABLE=$(gh pr view ${{ needs.check-conditions.outputs.comp-pr-number }} --repo "$COMPONENT_REPO" --json mergeable -q .mergeable) + echo "Mergeable state: $MERGEABLE" + if [ "$MERGEABLE" == "MERGEABLE" ]; then + break + fi + echo "πŸ” Waiting for GitHub to refresh mergeability..." + sleep 3 + done + + - name: Merge PR in component repo + run: | + echo "βœ… Merging component PR: ${{ needs.check-conditions.outputs.comp-url }}" + gh pr merge -R "$COMPONENT_REPO" ${{ needs.check-conditions.outputs.comp-pr-number }} --merge --delete-branch + + - name: Merge PR in appcat repo + run: | + echo "βœ… Merging appcat PR: https://github.com/$APPCAT_REPO/pull/${{ needs.check-conditions.outputs.pr-number }}" + gh pr merge -R "$APPCAT_REPO" ${{ needs.check-conditions.outputs.pr-number }} --merge --delete-branch + + merge-hotfix: + needs: check-conditions + if: needs.check-conditions.outputs.base-branch == 'master' && needs.check-conditions.outputs.is-hotfix == 'true' + runs-on: ubuntu-latest + env: + APPCAT_GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMPONENT_GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }} + steps: + - name: Merge PR in appcat repo + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "βœ… Merging appcat PR: https://github.com/$APPCAT_REPO/pull/${{ needs.check-conditions.outputs.pr-number }}" + gh pr merge -R "$APPCAT_REPO" ${{ needs.check-conditions.outputs.pr-number }} --merge --delete-branch + + - name: Auto-increment patch version and tag master + id: tag + run: | + git config --global user.email "githubbot@vshn.ch" + git config --global user.name "GitHubBot" + + git clone --quiet https://x-access-token:$APPCAT_GH_TOKEN@github.com/$APPCAT_REPO.git + cd appcat + git fetch --tags + + # Get the latest tag (assumes tags are in 'vX.Y.Z' format) + LATEST_TAG=$(git tag --sort=-creatordate | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1) + + if [ -z "$LATEST_TAG" ]; then + echo "No tags found." + exit 1 + else + echo "Latest tag: $LATEST_TAG" + MAJOR=$(echo "$LATEST_TAG" | cut -d. -f1 | sed 's/v//') + MINOR=$(echo "$LATEST_TAG" | cut -d. -f2) + PATCH=$(echo "$LATEST_TAG" | cut -d. -f3) + PATCH=$((PATCH + 1)) + NEW_TAG="v$MAJOR.$MINOR.$PATCH" + fi + + git checkout master + git tag "$NEW_TAG" + git push origin "$NEW_TAG" + echo "βœ… Created and pushed tag: $NEW_TAG" + echo "appcat-tag=$NEW_TAG" >> $GITHUB_OUTPUT + + - name: Clone and patch component repo + run: | + COMPONENT_BRANCH="${{ needs.check-conditions.outputs.comp-feature-branch }}" + APPCAT_NEW_TAG="${{ steps.tag.outputs.appcat-tag }}" + echo "πŸ”§ Using tag: $APPCAT_NEW_TAG" + echo "πŸ”§ Cloning branch: $COMPONENT_BRANCH" + + git clone https://x-access-token:$COMPONENT_GH_TOKEN@github.com/"$COMPONENT_REPO".git + cd component-appcat + git checkout "$COMPONENT_BRANCH" + + echo "πŸ”§ Patching class/defaults.yml..." + yq e ".parameters.appcat.images.appcat.tag = \"$APPCAT_NEW_TAG\"" class/defaults.yml \ + | diff -B class/defaults.yml - \ + | patch class/defaults.yml - || echo "βœ… No patch needed" + + make gen-golden-all + + git config --global user.email "githubbot@vshn.ch" + git config --global user.name "GitHubBot" + + git add . + if git diff --cached --quiet; then + echo "βœ… No changes to commit" + else + git commit -m "Auto update from appcat PR #${{ needs.check-conditions.outputs.pr-number }}, dependency $APPCAT_NEW_TAG" + git push origin "$COMPONENT_BRANCH" + fi + + - name: Retry mergeable check + env: + GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }} + run: | + for i in {1..5}; do + MERGEABLE=$(gh pr view ${{ needs.check-conditions.outputs.comp-pr-number }} --repo "$COMPONENT_REPO" --json mergeable -q .mergeable) + echo "Mergeable state: $MERGEABLE" + if [ "$MERGEABLE" == "MERGEABLE" ]; then + break + fi + echo "πŸ” Waiting for GitHub to refresh mergeability..." + sleep 3 + done + + - name: Merge PR in component repo + env: + GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }} + run: | + echo "βœ… Merging component PR: ${{ needs.check-conditions.outputs.comp-url }}" + gh pr merge -R "$COMPONENT_REPO" ${{ needs.check-conditions.outputs.comp-pr-number }} --merge --delete-branch + + merge-hotfix-update-develop: + needs: merge-hotfix + runs-on: ubuntu-latest + steps: + - name: Create PR to merge master into develop in appcat repo + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr create \ + --repo "$APPCAT_REPO" \ + --base develop \ + --head master \ + --reviewer ${{ needs.check-conditions.outputs.author }} \ \ + --title "πŸ”€ Merge master into develop (sync hotfixes)" \ + --body "This PR synchronizes the hotfixes from master back into develop." || true + + - name: Create PR to merge master into develop in component repo + env: + GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }} + run: | + gh pr create \ + --repo "$COMPONENT_REPO" \ + --base develop \ + --head master \ + --reviewer ${{ needs.check-conditions.outputs.author }} \ \ + --title "πŸ”€ Merge master into develop (sync hotfixes)" \ + --body "This PR synchronizes the hotfixes from master back into develop." || true + + - name: Check if masterβ†’develop PR is mergeable in appcat repo + id: check_mergeable_appcat + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_JSON=$(gh pr list --repo "$APPCAT_REPO" --head master --base develop --state open --json number,mergeable) + PR_NUMBER=$(echo "$PR_JSON" | jq -r '.[0].number') + MERGEABLE=$(echo "$PR_JSON" | jq -r '.[0].mergeable') + + if [ "$MERGEABLE" != "MERGEABLE" ]; then + echo "❌ PR #$PR_NUMBER is not mergeable (conflicts or unknown)." + exit 1 + fi + + echo "βœ… PR #$PR_NUMBER is mergeable." + echo "pr-number=$PR_NUMBER" >> $GITHUB_OUTPUT + + - name: Check if masterβ†’develop PR is mergeable in component repo + id: check_mergeable_component + env: + GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }} + run: | + PR_JSON=$(gh pr list --repo "$COMPONENT_REPO" --head master --base develop --state open --json number,mergeable) + PR_NUMBER=$(echo "$PR_JSON" | jq -r '.[0].number') + MERGEABLE=$(echo "$PR_JSON" | jq -r '.[0].mergeable') + + if [ "$MERGEABLE" != "MERGEABLE" ]; then + echo "❌ PR #$PR_NUMBER is not mergeable (conflicts or unknown)." + exit 1 + fi + + echo "βœ… PR #$PR_NUMBER is mergeable." + echo "pr-number=$PR_NUMBER" >> $GITHUB_OUTPUT + + - name: Merge PR in appcat repo + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "βœ… Merging appcat PR: https://github.com/$APPCAT_REPO/pull/${{ steps.check_mergeable_appcat.outputs.pr-number }}" + gh pr merge -R "$APPCAT_REPO" ${{ needs.check-conditions.outputs.pr-number }} --merge + + - name: Merge PR in component repo + env: + GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }} + run: | + echo "βœ… Merging component PR: https://github.com/$COMPONENT_REPO/pull/${{ steps.check_mergeable_component.outputs.pr-number }}" + gh pr merge -R "$COMPONENT_REPO" ${{ steps.check_mergeable_component.outputs.pr-number }} --merge + diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index b55b1f3..2f2a26f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -16,21 +16,45 @@ env: PUSH_IMAGE: "False" jobs: + check-allow-merge: + if: github.event.pull_request.base.ref != 'develop' || github.event.pull_request.head.ref != 'master' + runs-on: ubuntu-latest + steps: + # Labels in the context don't get updated so they are stuck at what's set during creation + # We need this action to get current labels + - name: Get current labels + uses: snnaplab/get-labels-action@v1 + - name: Check if merge is allowed + if: github.base_ref == 'master' && github.head_ref != 'develop' + run: | + ${{ contains(fromJSON(env.LABELS), 'hotfix') }} && exit 0 + echo "ERROR: You can only merge to master from develop or hotfixes." + exit 1 + check-labels: # Act doesn't set a pull request number by default, so we skip if it's 0 - if: github.event.pull_request.number != 0 + if: github.event.pull_request.number != 0 && (github.event.pull_request.base.ref != 'develop' || github.event.pull_request.head.ref != 'master') name: Check labels runs-on: ubuntu-latest steps: + - name: Ignore from Changelog if merge to master + uses: actions-ecosystem/action-add-labels@v1 + if: github.event.action != 'opened' && github.base_ref == 'master' + with: + labels: ignoreChangelog + - uses: docker://agilepathway/pull-request-label-checker:v1.6.51 with: one_of: major,minor,patch,documentation,dependency repo_token: ${{ secrets.GITHUB_TOKEN }} + publish-branch-images: - if: github.event.action != 'closed' + if: github.event.action != 'closed' && (github.event.pull_request.base.ref != 'develop' || github.event.pull_request.head.ref != 'master') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} - name: Determine Go version from go.mod run: echo "GO_VERSION=$(grep "go 1." go.mod | cut -d " " -f 2)" >> $GITHUB_ENV @@ -60,11 +84,15 @@ jobs: - name: Build branch and push image if: env.PUSH_IMAGE == 'true' - run: make docker-push-branchtag -e IMG_TAG="${{ steps.extract_branch.outputs.branch }}" + run: | + make docker-push-branchtag -e IMG_TAG="${{ steps.extract_branch.outputs.branch }}" + make docker-push-branchtag -e IMG_TAG="${{ github.event.pull_request.head.sha }}" - name: Build branch and push package if: env.PUSH_PACKAGE == 'True' - run: make package-push-branchtag -e IMG_TAG="${{ steps.extract_branch.outputs.branch }}" + run: | + make package-push-branchtag -e IMG_TAG="${{ steps.extract_branch.outputs.branch }}" + make package-push-branchtag -e IMG_TAG="${{ github.event.pull_request.head.sha }}" - name: Login to Upbound if: env.PUSH_UPBOUND == 'true' @@ -79,14 +107,18 @@ jobs: run: make package-push-branchtag -e IMG_TAG="${{ steps.extract_branch.outputs.branch }}" -e IMG_REPO=xpkg.upbound.io open-pr-component: - if: github.event.action == 'opened' runs-on: ubuntu-latest + if: github.event.pull_request.number != 0 && github.event.action == 'opened' && (github.event.pull_request.base.ref != 'develop' || github.event.pull_request.head.ref != 'master') steps: + - name: Get current labels + uses: snnaplab/get-labels-action@v1 + - name: Checkout code uses: actions/checkout@v4 with: repository: ${{ env.COMPONENT_REPO }} token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 - name: Extract branch name shell: bash @@ -102,116 +134,39 @@ jobs: - name: Generate new golden # Act uses the host's docker to run containers, but then # they can't access the files that were previously cloned. - if: github.event.pull_request.number != 0 + if: github.event.pull_request.number != 0 && github.event.action == 'opened' run: | make gen-golden-all + - name: Check if it's a hotfix + id: hotfix_check + run: | + ${{ contains(fromJSON(env.LABELS), 'hotfix') }} && echo "base=master" >> $GITHUB_OUTPUT || echo "base=develop" >> $GITHUB_OUTPUT + - name: Create Pull Request - uses: peter-evans/create-pull-request@v6 + uses: peter-evans/create-pull-request@v7 + id: cpr with: token: ${{ secrets.COMPONENT_ACCESS_TOKEN }} - title: 'PR for ${{ env.APP_NAME }} on ${{ steps.extract_branch.outputs.branch }}' + title: ${{ github.event.pull_request.title }} body: "${{ github.event.pull_request.body}}\nLink: ${{ github.event.pull_request.html_url }}" branch: "${{ env.APP_NAME }}/${{ github.event.pull_request.number }}/${{ steps.extract_branch.outputs.branch }}" - base: master + base: ${{ steps.hotfix_check.outputs.base }} draft: false - create-release: - if: github.event.pull_request.merged - runs-on: ubuntu-latest - steps: - - name: Check for patch label - if: contains(github.event.pull_request.labels.*.name, 'patch') || contains(github.event.pull_request.labels.*.name, 'dependency') || contains(github.event.pull_request.labels.*.name, 'documentation') - id: patch - run: | - echo "set=true" >> $GITHUB_OUTPUT - - name: Check for minor label - if: contains(github.event.pull_request.labels.*.name, 'minor') - id: minor - run: | - echo "set=true" >> $GITHUB_OUTPUT - - name: Check for major label - if: contains(github.event.pull_request.labels.*.name, 'major') - id: major - run: | - echo "set=true" >> $GITHUB_OUTPUT - - uses: actions/checkout@v4 + - name: Add link to component PR + uses: tzkhan/pr-update-action@v2 with: - # Make sure we use the right commit to tag - ref: ${{ github.event.pull_request.merge_commit_sha }} - # We also need to use the personal access token here. As subsequent - # actions will not trigger by tags/pushes that use `GITHUB_TOKEN` - # https://github.com/orgs/community/discussions/25702#discussioncomment-3248819 - token: ${{ secrets.COMPONENT_ACCESS_TOKEN }} - # This is broken in checkout@v4... - # https://github.com/actions/checkout/issues/1781 - fetch-tags: true - - - name: fetch tags - run: | - git fetch --tags - echo "latest tag: $(git describe --tags "$(git rev-list --tags --max-count=1)")" - echo "TAG_VERSION=$(git describe --tags "$(git rev-list --tags --max-count=1)")" >> $GITHUB_ENV - - - name: Extract branch name - shell: bash - run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT - id: extract_branch - - # We only run this if any of the release tags is set. - # For docs and deps we don't do automagic releases - - name: Increase Tag - id: tag - run: | - patch=${{ steps.patch.outputs.set }} - minor=${{ steps.minor.outputs.set }} - major=${{ steps.major.outputs.set }} - - major_ver=$(echo '${{ env.TAG_VERSION }}' | cut -d "." -f1) - minor_ver=$(echo '${{ env.TAG_VERSION }}' | cut -d "." -f2) - patch_ver=$(echo '${{ env.TAG_VERSION }}' | cut -d "." -f3) - - major_ver="${major_ver:1}" - - # Check for patch label - [ ! -z "$patch" ] && [ -z "$minor" ] && [ -z "$major" ] && ((patch_ver++)) || true - - # check for minor label - if [ ! -z "$minor" ] && [ -z "$major" ]; then - ((minor_ver++)) - patch_ver=0 - fi - - # Check for major label - if [ ! -z "$major" ]; then - ((major_ver++)) - minor_ver=0 - patch_ver=0 - fi - - tag="v$major_ver.$minor_ver.$patch_ver" - echo "new tag $tag" - git tag $tag - git push --tags - echo tag=$tag >> $GITHUB_OUTPUT - - - name: Checkout component - uses: actions/checkout@v4 + repo-token: "${{ secrets.GITHUB_TOKEN }}" + head-branch-regex: "${{ steps.extract_branch.outputs.branch }}" + body-template: | + Component PR: ${{ steps.cpr.outputs.pull-request-url }} + body-update-action: 'suffix' + body-uppercase-base-match: false + + - name: Ignore from Changelog if merge to master + uses: actions-ecosystem/action-add-labels@v1 + if: github.base_ref == 'master' with: - repository: ${{ env.COMPONENT_REPO }} - token: ${{ secrets.COMPONENT_ACCESS_TOKEN }} - ref: "${{ env.APP_NAME }}/${{ github.event.pull_request.number }}/${{ steps.extract_branch.outputs.branch }}" - - - name: Update tag and run golden - run: | - yq e '.parameters.appcat.images.${{ env.APP_NAME }}.tag="${{ steps.tag.outputs.tag }}"' class/defaults.yml | diff -B class/defaults.yml - | patch class/defaults.yml - || true - make gen-golden-all - - - name: Commit & Push changes - uses: actions-js/push@master - with: - github_token: ${{ secrets.COMPONENT_ACCESS_TOKEN }} - branch: "${{ env.APP_NAME }}/${{ github.event.pull_request.number }}/${{ steps.extract_branch.outputs.branch }}" - message: "Update tag" - repository: ${{ env.COMPONENT_REPO }} + labels: ignoreChangelog diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 0000000..0c10f2c --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,142 @@ +name: Pre Release + +on: + workflow_dispatch: # Allow manual trigger + +env: + APP_NAME: provider-exoscale + COMPONENT_REPO: vshn/component-appcat + APPCAT_REPO: vshn/appcat + +jobs: + create-and-merge-appcat-pr: + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + outputs: + new_tag: ${{ steps.bump.outputs.new_tag }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + + - name: Create PR from develop to master + id: create_pr + run: | + gh pr create \ + --repo $APPCAT_REPO \ + --base master \ + --head develop \ + --title "πŸ”€ Merge develop into master (Release)" \ + --body "Auto-generated PR to release from develop to master." \ + || echo "PR already exists or failed" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get PR ID + id: pr_info + run: | + PR_JSON=$(gh pr list --repo $APPCAT_REPO --head develop --base master --state open --json number) + echo "pr_number=$(echo $PR_JSON | jq -r '.[0].number')" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Retry mergeable check + id: pr_mergeable + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + for i in {1..100}; do + MERGEABLE=$(gh pr view ${{ steps.pr_info.outputs.pr_number }} --repo "$APPCAT_REPO" --json mergeable -q .mergeable) + echo "Mergeable state: $MERGEABLE" + if [ "$MERGEABLE" == "MERGEABLE" ]; then + break + fi + echo "πŸ” Waiting for GitHub to refresh mergeability..." + sleep 5 + done + echo "mergeable=$MERGEABLE" >> "$GITHUB_OUTPUT" + + - name: Fail if not mergeable + if: steps.pr_mergeable.outputs.mergeable != 'MERGEABLE' + run: | + echo "PR #${{ steps.pr_info.outputs.pr_number }} is not mergeable!" + exit 1 + + - name: Merge PR + run: gh pr merge ${{ steps.pr_info.outputs.pr_number }} --merge --admin + env: + GH_TOKEN: ${{ secrets.MERGE_TOKEN }} + + - name: Bump minor version and tag + id: bump + run: | + LATEST_TAG=$(git describe --tags --abbrev=0) + MAJOR=$(echo "$LATEST_TAG" | cut -d. -f1) + MINOR=$(echo "$LATEST_TAG" | cut -d. -f2) + NEW_TAG="$MAJOR.$((MINOR + 1)).0" + git tag $NEW_TAG + git push origin $NEW_TAG + echo "new_tag=$NEW_TAG" >> $GITHUB_OUTPUT + + - name: Fast forward develop to master + run: | + git checkout develop + git merge master --ff-only + git push origin develop + env: + GH_TOKEN: ${{ secrets.MERGE_TOKEN }} + + create-component-release-pr: + needs: create-and-merge-appcat-pr + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }} + steps: + - name: Clone component repo + uses: actions/checkout@v4 + with: + repository: ${{ env.COMPONENT_REPO }} + token: ${{ secrets.COMPONENT_ACCESS_TOKEN }} + ref: develop + fetch-depth: 0 + + - name: Configure Git + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + + - name: Update version in class/defaults.yaml + run: | + echo "πŸ”§ Patching class/defaults.yml..." + TAG="${{ needs.create-and-merge-appcat-pr.outputs.new_tag }}" + yq e ".parameters.appcat.images.appcat.tag = \"$TAG\"" class/defaults.yml \ + | diff -B class/defaults.yml - \ + | patch class/defaults.yml - || echo "βœ… No patch needed" + + - name: Run make gen-golden-all + run: make gen-golden-all + + - name: Commit and push changes + run: | + git add . + git commit -m "Update version to ${{ needs.create-and-merge-appcat-pr.outputs.new_tag }}" + git push origin develop + + - name: Create PR in component repo + run: | + gh pr create \ + --repo "$COMPONENT_REPO" \ + --base master \ + --head develop \ + --title "⬆️ Update appcat version to ${{ needs.create-and-merge-appcat-pr.outputs.new_tag }}" \ + --body "This PR updates the version in defaults.yaml and regenerates golden files." + env: + GH_TOKEN: ${{ secrets.COMPONENT_ACCESS_TOKEN }} +