From 66d618206a39664579b7d0a616f7d2570ae77062 Mon Sep 17 00:00:00 2001 From: nachog00 Date: Tue, 24 Feb 2026 17:32:37 -0300 Subject: [PATCH 1/6] Hotfix: fix issue 1 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8ad2dea..f1da2df 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ # Release Flow Test Repo +hotfix for issue 1 From 6c7f09328c4a86cc753a45c9decd1c6b37c3db71 Mon Sep 17 00:00:00 2001 From: nachog00 Date: Tue, 24 Feb 2026 17:35:56 -0300 Subject: [PATCH 2/6] Fix: replace symlinks with actual workflow files --- .github/workflows/auto-tag-rc.yml | 42 +++++++- .github/workflows/final-tag-on-stable.yml | 46 ++++++++- .github/workflows/release.yaml | 105 ++++++++++++++++++- .github/workflows/sync-rc-to-dev.yml | 118 +++++++++++++++++++++- 4 files changed, 307 insertions(+), 4 deletions(-) mode change 120000 => 100644 .github/workflows/auto-tag-rc.yml mode change 120000 => 100644 .github/workflows/final-tag-on-stable.yml mode change 120000 => 100644 .github/workflows/release.yaml mode change 120000 => 100644 .github/workflows/sync-rc-to-dev.yml diff --git a/.github/workflows/auto-tag-rc.yml b/.github/workflows/auto-tag-rc.yml deleted file mode 120000 index 22d8593..0000000 --- a/.github/workflows/auto-tag-rc.yml +++ /dev/null @@ -1 +0,0 @@ -/home/chona/zingo/zingolabs/zaino/dev/.github/workflows/auto-tag-rc.yml \ No newline at end of file diff --git a/.github/workflows/auto-tag-rc.yml b/.github/workflows/auto-tag-rc.yml new file mode 100644 index 0000000..9c14641 --- /dev/null +++ b/.github/workflows/auto-tag-rc.yml @@ -0,0 +1,41 @@ +name: Auto RC Tag + +on: + pull_request: + types: [closed] + branches: + - 'rc/*' + +jobs: + auto-tag-rc: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get next RC tag + id: tagger + run: | + # Use base ref (the target branch) since github.ref points to refs/pull/*/merge during PR events + VERSION=$(echo "${{ github.event.pull_request.base.ref }}" | sed -E 's#rc/##') + PREFIX="$VERSION-rc" + + echo "Looking for previous tags matching $PREFIX.*" + git fetch --tags + + LATEST=$(git tag -l "$PREFIX.*" | sed -E "s/.*rc\.//" | sort -n | tail -1) + NEXT=$((LATEST + 1)) + TAG="$PREFIX.$NEXT" + + echo "TAG=$TAG" >> $GITHUB_OUTPUT + + - name: Create and push tag + run: | + git config user.name github-actions + git config user.email github-actions@users.noreply.github.com + git tag ${{ steps.tagger.outputs.TAG }} + git push origin ${{ steps.tagger.outputs.TAG }} diff --git a/.github/workflows/final-tag-on-stable.yml b/.github/workflows/final-tag-on-stable.yml deleted file mode 120000 index 4d0dd21..0000000 --- a/.github/workflows/final-tag-on-stable.yml +++ /dev/null @@ -1 +0,0 @@ -/home/chona/zingo/zingolabs/zaino/dev/.github/workflows/final-tag-on-stable.yml \ No newline at end of file diff --git a/.github/workflows/final-tag-on-stable.yml b/.github/workflows/final-tag-on-stable.yml new file mode 100644 index 0000000..93a7e45 --- /dev/null +++ b/.github/workflows/final-tag-on-stable.yml @@ -0,0 +1,45 @@ +name: Final Tag on Stable + +on: + pull_request: + types: [closed] + branches: + - stable + +jobs: + tag-stable: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: read + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Lookup merged PR and extract version + id: get-version + uses: actions/github-script@v7 + with: + script: | + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.number, + }); + + const baseBranch = pr.data.base.ref; + const headBranch = pr.data.head.ref; + + if (!headBranch.startsWith("rc/")) throw new Error("PR is not from an rc/* branch"); + const version = headBranch.replace(/^rc\//, ""); + core.setOutput("version", version); + + - name: Create final release tag + run: | + git config user.name github-actions + git config user.email github-actions@users.noreply.github.com + git tag ${{ steps.get-version.outputs.version }} + git push origin ${{ steps.get-version.outputs.version }} + diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 120000 index a14f2bc..0000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1 +0,0 @@ -/home/chona/zingo/zingolabs/zaino/dev/.github/workflows/release.yaml \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..2e1bd70 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,104 @@ +name: Zaino Release + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+' + - '[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+' + workflow_dispatch: + +jobs: + docker: + name: Docker images (build+push) + runs-on: ubuntu-latest + env: + PUSH_IMAGES: true + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} + + - name: Extract metadata for Docker (default image) + id: meta-default + uses: docker/metadata-action@v5 + with: + images: ${{ secrets.DOCKERHUB_USERNAME }}/${{ vars.DOCKER_RELEASE_REPO || 'zaino' }} + tags: | + type=semver,pattern={{version}} + + - name: Build and Push Default Image + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64 + push: ${{ env.PUSH_IMAGES }} + tags: ${{ steps.meta-default.outputs.tags }} + labels: ${{ steps.meta-default.outputs.labels }} + cache-from: | + type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/${{ vars.DOCKER_RELEASE_REPO || 'zaino' }}:buildcache + type=gha + cache-to: | + type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/${{ vars.DOCKER_RELEASE_REPO || 'zaino' }}:buildcache,mode=max + type=gha,mode=max + + - name: Extract metadata for Docker (no-tls image) + id: meta-no-tls + uses: docker/metadata-action@v5 + with: + images: ${{ secrets.DOCKERHUB_USERNAME }}/${{ vars.DOCKER_RELEASE_REPO || 'zaino' }} + flavor: | + suffix=-no-tls + tags: | + type=semver,pattern={{version}} + + - name: Build and Push No-TLS Image + uses: docker/build-push-action@v5 + with: + build-args: NO_TLS=true + context: . + platforms: linux/amd64 + push: ${{ env.PUSH_IMAGES }} + tags: "${{ steps.meta-no-tls.outputs.tags }}" + labels: ${{ steps.meta-no-tls.outputs.labels }} + cache-from: | + type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/${{ vars.DOCKER_RELEASE_REPO || 'zaino' }}:buildcache-no-tls + type=gha + cache-to: | + type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/${{ vars.DOCKER_RELEASE_REPO || 'zaino' }}:buildcache-no-tls,mode=max + type=gha,mode=max + + create-release: + needs: docker + name: Create GitHub Release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Generate Changelog + id: changelog + run: | + PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") + if [ -z "$PREVIOUS_TAG" ]; then + git log --pretty=format:"* %s" > CHANGELOG.md + else + git log --pretty=format:"* %s" $PREVIOUS_TAG..HEAD > CHANGELOG.md + fi + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + files: | + CHANGELOG.md + body_path: CHANGELOG.md + draft: false + prerelease: ${{ contains(github.ref, '-rc.') }} + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/sync-rc-to-dev.yml b/.github/workflows/sync-rc-to-dev.yml deleted file mode 120000 index 117086d..0000000 --- a/.github/workflows/sync-rc-to-dev.yml +++ /dev/null @@ -1 +0,0 @@ -/home/chona/zingo/zingolabs/zaino/dev/.github/workflows/sync-rc-to-dev.yml \ No newline at end of file diff --git a/.github/workflows/sync-rc-to-dev.yml b/.github/workflows/sync-rc-to-dev.yml new file mode 100644 index 0000000..2049030 --- /dev/null +++ b/.github/workflows/sync-rc-to-dev.yml @@ -0,0 +1,117 @@ +name: Sync RC to Dev + +on: + # Triggered after an RC tag is created + push: + tags: + - '*-rc.*' + # Allow manual trigger + workflow_dispatch: + inputs: + rc_branch: + description: 'RC branch to sync from (e.g., rc/0.9.0)' + required: true + type: string + +jobs: + sync-rc-to-dev: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + # Use a token with PR creation permissions + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Determine RC branch + id: get-rc-branch + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + RC_BRANCH="${{ github.event.inputs.rc_branch }}" + else + # Extract version from tag (e.g., 0.9.0-rc.1 -> 0.9.0) + TAG="${GITHUB_REF#refs/tags/}" + VERSION=$(echo "$TAG" | sed -E 's/-rc\.[0-9]+$//') + RC_BRANCH="rc/$VERSION" + fi + echo "rc_branch=$RC_BRANCH" >> $GITHUB_OUTPUT + + - name: Check if sync is needed + id: check-sync + run: | + git fetch origin dev "${{ steps.get-rc-branch.outputs.rc_branch }}" + + # Check if there are commits in rc that aren't in dev + COMMITS_AHEAD=$(git rev-list --count origin/dev..origin/${{ steps.get-rc-branch.outputs.rc_branch }}) + + if [ "$COMMITS_AHEAD" -eq 0 ]; then + echo "No commits to sync from ${{ steps.get-rc-branch.outputs.rc_branch }} to dev" + echo "needs_sync=false" >> $GITHUB_OUTPUT + else + echo "$COMMITS_AHEAD commits ahead - sync needed" + echo "needs_sync=true" >> $GITHUB_OUTPUT + fi + + - name: Create sync PR + if: steps.check-sync.outputs.needs_sync == 'true' + uses: actions/github-script@v7 + with: + script: | + const rcBranch = '${{ steps.get-rc-branch.outputs.rc_branch }}'; + const tag = process.env.GITHUB_REF?.replace('refs/tags/', '') || 'manual sync'; + + // Find and close any existing sync PRs for this RC branch (superseded by the new one) + const existingPRs = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + head: `${context.repo.owner}:${rcBranch}`, + base: 'dev' + }); + + for (const oldPR of existingPRs.data) { + console.log(`Closing superseded sync PR #${oldPR.number}`); + + // Add a comment explaining why it's being closed + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: oldPR.number, + body: `Closing this PR as it has been superseded by a newer RC tag: \`${tag}\`.\n\nA new sync PR will be created with all the latest changes.` + }); + + // Close the old PR + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: oldPR.number, + state: 'closed' + }); + } + + // Create the new PR + const pr = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: `Sync ${rcBranch} to dev`, + body: `## Automated RC Sync + + This PR syncs changes from \`${rcBranch}\` back to \`dev\` to prevent branch deviation. + + **Triggered by:** ${tag} + + ### Review Notes + - Please review any hotfix changes that were applied directly to the RC branch + - Resolve any merge conflicts that may arise + - This helps ensure dev stays up-to-date with release fixes + + --- + *This PR was automatically created by the sync-rc-to-dev workflow.*`, + head: rcBranch, + base: 'dev' + }); + + console.log(`Created PR #${pr.data.number}: ${pr.data.html_url}`); From ac03b7d362dd33fef6151d99ad33449eaa43cfcd Mon Sep 17 00:00:00 2001 From: nachog00 Date: Tue, 24 Feb 2026 17:36:12 -0300 Subject: [PATCH 3/6] Hotfix: fix issue 2 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f1da2df..e5279c8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # Release Flow Test Repo hotfix for issue 1 +hotfix for issue 2 From ed5e7894e956510ac1baa8db90a123def874a920 Mon Sep 17 00:00:00 2001 From: nachog00 Date: Tue, 24 Feb 2026 17:39:50 -0300 Subject: [PATCH 4/6] Combine auto-tag and sync-to-dev into single workflow --- .github/workflows/auto-tag-rc.yml | 94 ++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/.github/workflows/auto-tag-rc.yml b/.github/workflows/auto-tag-rc.yml index 9c14641..f9408fd 100644 --- a/.github/workflows/auto-tag-rc.yml +++ b/.github/workflows/auto-tag-rc.yml @@ -12,6 +12,9 @@ jobs: runs-on: ubuntu-latest permissions: contents: write + outputs: + tag: ${{ steps.tagger.outputs.TAG }} + rc_branch: ${{ steps.tagger.outputs.RC_BRANCH }} steps: - uses: actions/checkout@v4 with: @@ -21,7 +24,8 @@ jobs: id: tagger run: | # Use base ref (the target branch) since github.ref points to refs/pull/*/merge during PR events - VERSION=$(echo "${{ github.event.pull_request.base.ref }}" | sed -E 's#rc/##') + RC_BRANCH="${{ github.event.pull_request.base.ref }}" + VERSION=$(echo "$RC_BRANCH" | sed -E 's#rc/##') PREFIX="$VERSION-rc" echo "Looking for previous tags matching $PREFIX.*" @@ -32,6 +36,7 @@ jobs: TAG="$PREFIX.$NEXT" echo "TAG=$TAG" >> $GITHUB_OUTPUT + echo "RC_BRANCH=$RC_BRANCH" >> $GITHUB_OUTPUT - name: Create and push tag run: | @@ -39,3 +44,90 @@ jobs: git config user.email github-actions@users.noreply.github.com git tag ${{ steps.tagger.outputs.TAG }} git push origin ${{ steps.tagger.outputs.TAG }} + + sync-rc-to-dev: + needs: auto-tag-rc + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check if sync is needed + id: check-sync + run: | + RC_BRANCH="${{ needs.auto-tag-rc.outputs.rc_branch }}" + git fetch origin dev "$RC_BRANCH" + + # Check if there are commits in rc that aren't in dev + COMMITS_AHEAD=$(git rev-list --count origin/dev..origin/$RC_BRANCH) + + if [ "$COMMITS_AHEAD" -eq 0 ]; then + echo "No commits to sync from $RC_BRANCH to dev" + echo "needs_sync=false" >> $GITHUB_OUTPUT + else + echo "$COMMITS_AHEAD commits ahead - sync needed" + echo "needs_sync=true" >> $GITHUB_OUTPUT + fi + + - name: Create sync PR + if: steps.check-sync.outputs.needs_sync == 'true' + uses: actions/github-script@v7 + with: + script: | + const rcBranch = '${{ needs.auto-tag-rc.outputs.rc_branch }}'; + const tag = '${{ needs.auto-tag-rc.outputs.tag }}'; + + // Find and close any existing sync PRs for this RC branch (superseded by the new one) + const existingPRs = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + head: `${context.repo.owner}:${rcBranch}`, + base: 'dev' + }); + + for (const oldPR of existingPRs.data) { + console.log(`Closing superseded sync PR #${oldPR.number}`); + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: oldPR.number, + body: `Closing this PR as it has been superseded by a newer RC tag: \`${tag}\`.\n\nA new sync PR will be created with all the latest changes.` + }); + + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: oldPR.number, + state: 'closed' + }); + } + + // Create the new PR + const pr = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: `Sync ${rcBranch} to dev`, + body: `## Automated RC Sync + + This PR syncs changes from \`${rcBranch}\` back to \`dev\` to prevent branch deviation. + + **Triggered by:** ${tag} + + ### Review Notes + - Please review any hotfix changes that were applied directly to the RC branch + - Resolve any merge conflicts that may arise + - This helps ensure dev stays up-to-date with release fixes + + --- + *This PR was automatically created by the sync-rc-to-dev workflow.*`, + head: rcBranch, + base: 'dev' + }); + + console.log(`Created PR #${pr.data.number}: ${pr.data.html_url}`); From 9b4e93fafb986a79abeaf7e47ea70429d7c96483 Mon Sep 17 00:00:00 2001 From: nachog00 Date: Tue, 24 Feb 2026 17:40:08 -0300 Subject: [PATCH 5/6] Hotfix: fix issue 3 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e5279c8..b2ee18f 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ # Release Flow Test Repo hotfix for issue 1 hotfix for issue 2 +hotfix for issue 3 From e25e3d79367ca65e89b370c474c3406ed3c6b26c Mon Sep 17 00:00:00 2001 From: nachog00 Date: Tue, 24 Feb 2026 17:48:05 -0300 Subject: [PATCH 6/6] Make Docker builds conditional on secrets being configured --- .github/workflows/release.yaml | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2e1bd70..791d75c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,4 +1,4 @@ -name: Zaino Release +name: Release on: push: @@ -8,8 +8,27 @@ on: workflow_dispatch: jobs: + # Check if Docker secrets are configured + check-docker-config: + name: Check Docker configuration + runs-on: ubuntu-latest + outputs: + docker_configured: ${{ steps.check.outputs.configured }} + steps: + - name: Check for Docker secrets + id: check + run: | + if [ -n "${{ secrets.DOCKERHUB_USERNAME }}" ] && [ -n "${{ secrets.DOCKERHUB_ACCESS_TOKEN }}" ]; then + echo "configured=true" >> $GITHUB_OUTPUT + else + echo "configured=false" >> $GITHUB_OUTPUT + echo "::notice::Docker secrets not configured - skipping Docker builds" + fi + docker: name: Docker images (build+push) + needs: check-docker-config + if: needs.check-docker-config.outputs.docker_configured == 'true' runs-on: ubuntu-latest env: PUSH_IMAGES: true @@ -75,7 +94,9 @@ jobs: type=gha,mode=max create-release: - needs: docker + needs: [check-docker-config, docker] + # Run even if docker was skipped (but not if it failed) + if: always() && (needs.docker.result == 'success' || needs.docker.result == 'skipped') name: Create GitHub Release runs-on: ubuntu-latest steps: