diff --git a/.github/workflows/chart-ci.yml b/.github/workflows/chart-ci.yml new file mode 100644 index 000000000..345cb1f6d --- /dev/null +++ b/.github/workflows/chart-ci.yml @@ -0,0 +1,38 @@ +name: Chart CI + +on: + pull_request: + paths: + - 'charts/**' + - '.github/workflows/chart-ci.yml' + push: + branches: + - main + paths: + - 'charts/**' + - '.github/workflows/chart-ci.yml' + +jobs: + helm-checks: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Helm + uses: azure/setup-helm@v4.3.1 + + - name: Add chart dependencies repositories + run: | + helm repo add cloudnative-pg https://cloudnative-pg.github.io/charts + helm repo add ot-helm https://ot-container-kit.github.io/helm-charts + helm repo update + + - name: Build chart dependencies + run: helm dependency build charts/sure + + - name: Lint chart + run: helm lint charts/sure + + - name: Render templates + run: helm template sure charts/sure --kube-version 1.25.0 >/tmp/sure-chart-rendered.yaml diff --git a/.github/workflows/chart-release.yml b/.github/workflows/chart-release.yml new file mode 100644 index 000000000..5fb71d32d --- /dev/null +++ b/.github/workflows/chart-release.yml @@ -0,0 +1,110 @@ +name: Chart Release + +on: + push: + tags: + - 'chart-v*' + workflow_dispatch: + +permissions: + contents: write + +jobs: + prepare_release: + runs-on: ubuntu-latest + outputs: + tag_name: ${{ steps.tag.outputs.tag_name }} + chart_version: ${{ steps.tag.outputs.chart_version }} + app_version: ${{ steps.tag.outputs.app_version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Resolve chart release tag + id: tag + shell: bash + run: | + set -euo pipefail + + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + BASE_STABLE_TAG="$(git tag --list 'v*' --sort=-v:refname | { grep -Ev 'alpha|beta|rc' || true; } | head -n 1)" + if [ -z "$BASE_STABLE_TAG" ]; then + echo "::error::No stable app tag found for chart appVersion" + exit 1 + fi + + LATEST_CHART_TAG="$(git tag --list 'chart-v*' --sort=-v:refname | head -n 1)" + if [ -n "$LATEST_CHART_TAG" ]; then + BASE_CHART_VERSION="${LATEST_CHART_TAG#chart-v}" + else + BASE_CHART_VERSION="$(sed -n 's/^version: //p' charts/sure/Chart.yaml | head -n 1)" + BASE_CHART_VERSION="${BASE_CHART_VERSION:-0.0.0}" + fi + MAJOR="$(echo "$BASE_CHART_VERSION" | cut -d. -f1)" + MINOR="$(echo "$BASE_CHART_VERSION" | cut -d. -f2)" + PATCH="$(echo "$BASE_CHART_VERSION" | cut -d. -f3 | sed 's/[^0-9].*$//')" + PATCH="${PATCH:-0}" + + NEXT_PATCH=$((PATCH + 1)) + TAG_NAME="chart-v${MAJOR}.${MINOR}.${NEXT_PATCH}" + while git rev-parse "refs/tags/${TAG_NAME}" >/dev/null 2>&1; do + NEXT_PATCH=$((NEXT_PATCH + 1)) + TAG_NAME="chart-v${MAJOR}.${MINOR}.${NEXT_PATCH}" + done + CHART_VERSION="${MAJOR}.${MINOR}.${NEXT_PATCH}" + + git config user.name "${GITHUB_ACTOR}" + git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" + if ! git tag "$TAG_NAME"; then + echo "::error::Failed to create tag ${TAG_NAME}" + exit 1 + fi + if ! git push origin "$TAG_NAME"; then + echo "::error::Failed to push tag ${TAG_NAME} to origin" + exit 1 + fi + else + TAG_NAME="${GITHUB_REF_NAME}" + BASE_STABLE_TAG="$(git tag --list 'v*' --sort=-v:refname | grep -Ev 'alpha|beta|rc' | head -n 1 || true)" + fi + + CHART_VERSION="${TAG_NAME#chart-v}" + + if [ -z "${BASE_STABLE_TAG:-}" ]; then + echo "::warning::No stable app tag found; falling back to CHART_VERSION (${CHART_VERSION}) for appVersion" + fi + + APP_VERSION="${BASE_STABLE_TAG:-${CHART_VERSION}}" + + echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" + echo "chart_version=$CHART_VERSION" >> "$GITHUB_OUTPUT" + echo "app_version=$APP_VERSION" >> "$GITHUB_OUTPUT" + + publish_chart: + needs: prepare_release + uses: ./.github/workflows/helm-publish.yml + with: + chart_version: ${{ needs.prepare_release.outputs.chart_version }} + app_version: ${{ needs.prepare_release.outputs.app_version }} + update_gh_pages: true + secrets: inherit + + release: + needs: [prepare_release, publish_chart] + runs-on: ubuntu-latest + steps: + - name: Download Helm chart artifact + uses: actions/download-artifact@v4 + with: + name: helm-chart-package + path: ${{ runner.temp }}/helm-artifacts + + - name: Create chart GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ needs.prepare_release.outputs.tag_name }} + name: ${{ needs.prepare_release.outputs.tag_name }} + generate_release_notes: true + files: ${{ runner.temp }}/helm-artifacts/*.tgz diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index e553b213f..198a2ea41 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1,28 +1,6 @@ name: Flutter Mobile Build on: - push: - branches: - - main - tags: - - 'v*' - paths: - - 'mobile/lib/**' - - 'mobile/android/**' - - 'mobile/ios/**' - - 'mobile/pubspec.yaml' - - 'public/android-chrome-*.png' - - 'public/apple-touch-icon.png' - - '.github/workflows/flutter-build.yml' - pull_request: - paths: - - 'mobile/lib/**' - - 'mobile/android/**' - - 'mobile/ios/**' - - 'mobile/pubspec.yaml' - - 'public/android-chrome-*.png' - - 'public/apple-touch-icon.png' - - '.github/workflows/flutter-build.yml' workflow_call: workflow_dispatch: @@ -189,4 +167,4 @@ jobs: path: | mobile/build/ios/iphoneos/Runner.app mobile/build/ios-build-info.txt - retention-days: 30 \ No newline at end of file + retention-days: 30 diff --git a/.github/workflows/helm-publish.yml b/.github/workflows/helm-publish.yml new file mode 100644 index 000000000..4baeac91a --- /dev/null +++ b/.github/workflows/helm-publish.yml @@ -0,0 +1,160 @@ +name: Helm Publish + +on: + workflow_call: + inputs: + chart_version: + description: Chart semver version (v-prefix allowed) + required: false + type: string + app_version: + description: App version value for Chart.yaml appVersion + required: false + type: string + update_gh_pages: + description: Whether to publish packaged chart to gh-pages index + required: false + type: boolean + default: true + +permissions: + contents: write + +jobs: + publish: + if: github.repository == 'we-promise/sure' + runs-on: ubuntu-latest + outputs: + chart_version: ${{ steps.version.outputs.chart_version }} + app_version: ${{ steps.version.outputs.app_version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Helm + uses: azure/setup-helm@v4.3.1 + + - name: Resolve chart and app versions + id: version + shell: bash + run: | + set -euo pipefail + + normalize_version() { + local raw="$1" + echo "${raw#v}" + } + + if [ -n "${{ inputs.chart_version }}" ]; then + CHART_VERSION="$(normalize_version "${{ inputs.chart_version }}")" + elif [[ "${GITHUB_REF_TYPE}" == "tag" && "${GITHUB_REF_NAME}" == v* ]]; then + CHART_VERSION="$(normalize_version "${GITHUB_REF_NAME}")" + else + CHART_VERSION="0.0.0-nightly.$(date -u +'%Y%m%d.%H%M%S')" + fi + + if [ -n "${{ inputs.app_version }}" ]; then + APP_VERSION="${{ inputs.app_version }}" + elif [[ "${GITHUB_REF_TYPE}" == "tag" && "${GITHUB_REF_NAME}" == v* ]]; then + APP_VERSION="${GITHUB_REF_NAME}" + else + APP_VERSION="${CHART_VERSION}" + fi + + echo "chart_version=${CHART_VERSION}" >> "$GITHUB_OUTPUT" + echo "app_version=${APP_VERSION}" >> "$GITHUB_OUTPUT" + + - name: Update Chart.yaml version + shell: bash + run: | + set -euo pipefail + sed -i -E "s/^version:.*/version: ${{ steps.version.outputs.chart_version }}/" charts/sure/Chart.yaml + sed -i -E "s/^appVersion:.*/appVersion: \"${{ steps.version.outputs.app_version }}\"/" charts/sure/Chart.yaml + + - name: Add Helm repositories + run: | + helm repo add cloudnative-pg https://cloudnative-pg.github.io/charts + helm repo add ot-helm https://ot-container-kit.github.io/helm-charts + helm repo update + + - name: Build dependencies + run: helm dependency build charts/sure + + - name: Package chart + run: | + mkdir -p .cr-release-packages + helm package charts/sure -d .cr-release-packages + + - name: Upload packaged chart artifact + uses: actions/upload-artifact@v4 + with: + name: helm-chart-package + path: .cr-release-packages/*.tgz + if-no-files-found: error + retention-days: 7 + + - name: Checkout gh-pages + if: ${{ inputs.update_gh_pages }} + uses: actions/checkout@v4 + with: + ref: gh-pages + path: gh-pages + + - name: Update index and push + if: ${{ inputs.update_gh_pages }} + env: + GIT_USER_NAME: ${{ github.actor }} + GIT_USER_EMAIL: ${{ github.actor }}@users.noreply.github.com + run: | + set -euo pipefail + + CHART_VERSION="${{ steps.version.outputs.chart_version }}" + MAX_ATTEMPTS=5 + + cp .cr-release-packages/*.tgz gh-pages/ + + cd gh-pages + git config user.name "$GIT_USER_NAME" + git config user.email "$GIT_USER_EMAIL" + + index_and_commit() { + if [ -f index.yaml ]; then + helm repo index . --url https://we-promise.github.io/sure --merge index.yaml + else + helm repo index . --url https://we-promise.github.io/sure + fi + + git add . + if git diff --cached --quiet; then + echo "No Helm chart updates to publish." + return 1 + fi + git commit -m "Publish chart ${CHART_VERSION}" + } + + index_and_commit || exit 0 + + for attempt in $(seq 1 "$MAX_ATTEMPTS"); do + echo "Push attempt ${attempt}/${MAX_ATTEMPTS}..." + if git push; then + echo "Chart ${CHART_VERSION} published successfully." + exit 0 + fi + + if [ "$attempt" -eq "$MAX_ATTEMPTS" ]; then + echo "::error::Failed to push after ${MAX_ATTEMPTS} attempts" + exit 1 + fi + + backoff=$(( attempt * 2 )) + echo "Push failed; retrying in ${backoff}s after rebase..." + sleep "$backoff" + + git fetch origin gh-pages + git rebase origin/gh-pages + + git reset HEAD~1 --soft 2>/dev/null || true + index_and_commit || { echo "No changes after rebase."; exit 0; } + done diff --git a/.github/workflows/helm-release.yaml b/.github/workflows/helm-release.yaml deleted file mode 100644 index 9fbd90c29..000000000 --- a/.github/workflows/helm-release.yaml +++ /dev/null @@ -1,113 +0,0 @@ -name: Release Helm Chart (WIP) - -on: - push: - branches: - - main - paths: - - 'charts/**' - tags: - - 'v*' - workflow_dispatch: - -jobs: - release: - if: github.repository == 'we-promise/sure' - runs-on: ubuntu-latest - permissions: - contents: write - pages: write - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Configure Git - env: - GIT_USER_NAME: ${{ github.actor }} - GIT_USER_EMAIL: ${{ github.actor }}@users.noreply.github.com - run: | - git config user.name "$GIT_USER_NAME" - git config user.email "$GIT_USER_EMAIL" - - - name: Install Helm - uses: azure/setup-helm@v3 - - - name: Generate nightly version - id: version - run: | - # Generate version like: 0.0.0-nightly.20251213.173045 - if [[ "${GITHUB_REF_TYPE}" == "tag" && "${GITHUB_REF_NAME}" == v* ]]; then - VERSION="${GITHUB_REF_NAME#v}" - else - BASE_VERSION="$(git tag -l 'v*' | sed 's/^v//' | sort -V | tail -n 1)" - if [[ -z "${BASE_VERSION}" ]]; then - BASE_VERSION="0.0.0" - fi - VERSION="${BASE_VERSION}-nightly.$(date -u +'%Y%m%d.%H%M%S')" - fi - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Generated version: $VERSION" - - - name: Update Chart.yaml version - run: | - sed -i "s/^version:.*/version: ${{ steps.version.outputs.version }}/" charts/sure/Chart.yaml - sed -i "s/^appVersion:.*/appVersion: \"${{ steps.version.outputs.version }}\"/" charts/sure/Chart.yaml - cat charts/sure/Chart.yaml - - - name: Add Helm repositories - run: | - helm repo add cloudnative-pg https://cloudnative-pg.github.io/charts - helm repo add ot-helm https://ot-container-kit.github.io/helm-charts - helm repo update - - - name: Build dependencies - run: | - helm dependency build charts/sure - - - name: Package chart - run: | - mkdir -p .cr-release-packages - helm package charts/sure -d .cr-release-packages - - - name: Checkout gh-pages - uses: actions/checkout@v4 - with: - ref: gh-pages - path: gh-pages - - - name: Update index and push - env: - GIT_USER_NAME: ${{ github.actor }} - GIT_USER_EMAIL: ${{ github.actor }}@users.noreply.github.com - run: | - # Copy packaged chart - cp .cr-release-packages/*.tgz gh-pages/ - - # Update index - helm repo index gh-pages --url https://we-promise.github.io/sure --merge gh-pages/index.yaml - - # Push to gh-pages - git config --global credential.helper cache - cd gh-pages - git config user.name "$GIT_USER_NAME" - git config user.email "$GIT_USER_EMAIL" - git add . - if git diff --cached --quiet; then - echo "No Helm chart updates to publish." - exit 0 - fi - if [[ "${GITHUB_REF_TYPE}" == "tag" && "${GITHUB_REF_NAME}" == v* ]]; then - git commit -m "Release chart for ${{ github.ref_name }}" - else - git commit -m "Release nightly: ${{ steps.version.outputs.version }}" - fi - git push - - - name: Upload chart to GitHub Release - if: startsWith(github.ref, 'refs/tags/v') - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ github.ref_name }} - files: .cr-release-packages/*.tgz diff --git a/.github/workflows/mobile-ci.yml b/.github/workflows/mobile-ci.yml new file mode 100644 index 000000000..d7d391a6b --- /dev/null +++ b/.github/workflows/mobile-ci.yml @@ -0,0 +1,28 @@ +name: Mobile CI + +on: + pull_request: + paths: + - 'mobile/**' + - 'public/android-chrome-*.png' + - 'public/apple-touch-icon.png' + - '.github/workflows/flutter-build.yml' + - '.github/workflows/mobile-ci.yml' + push: + branches: + - main + paths: + - 'mobile/**' + - 'public/android-chrome-*.png' + - 'public/apple-touch-icon.png' + - '.github/workflows/flutter-build.yml' + - '.github/workflows/mobile-ci.yml' + +concurrency: + group: mobile-ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + uses: ./.github/workflows/flutter-build.yml + secrets: inherit diff --git a/.github/workflows/mobile-release.yml b/.github/workflows/mobile-release.yml index 7bce40812..5de88a6d3 100644 --- a/.github/workflows/mobile-release.yml +++ b/.github/workflows/mobile-release.yml @@ -33,9 +33,14 @@ jobs: set -euo pipefail if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - BRANCH_NAME="${GITHUB_REF_NAME}" - SAFE_BRANCH_NAME="${BRANCH_NAME//\//-}" - BASE_TAG="${SAFE_BRANCH_NAME}-$(date -u +'%Y%m%d%H%M')" + BASE_STABLE_TAG="$(git tag --list 'v*' --sort=-v:refname | { grep -Ev 'alpha|beta|rc' || true; } | head -n 1)" + if [ -z "$BASE_STABLE_TAG" ]; then + echo "::error::No stable v* tag found to base mobile patch release on" + exit 1 + fi + + STAMP="$(date -u +'%Y%m%d%H%M')" + BASE_TAG="mobile-${BASE_STABLE_TAG}-p${STAMP}" TAG_NAME="$BASE_TAG" TAG_SUFFIX=1 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 83e052db7..12473d176 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -242,6 +242,17 @@ jobs: sleep ${delay} done + + helm: + name: Package Helm chart + if: startsWith(github.ref, 'refs/tags/v') + uses: ./.github/workflows/helm-publish.yml + with: + chart_version: ${{ github.ref_name }} + app_version: ${{ github.ref_name }} + update_gh_pages: true + secrets: inherit + mobile: name: Build Mobile Apps if: startsWith(github.ref, 'refs/tags/v') @@ -251,7 +262,7 @@ jobs: release: name: Create GitHub Release if: startsWith(github.ref, 'refs/tags/v') - needs: [merge, mobile] + needs: [merge, mobile, helm] runs-on: ubuntu-latest timeout-minutes: 10 @@ -271,6 +282,12 @@ jobs: name: ios-build-unsigned path: ${{ runner.temp }}/ios-build + - name: Download Helm chart artifact + uses: actions/download-artifact@v4.3.0 + with: + name: helm-chart-package + path: ${{ runner.temp }}/helm-artifacts + - name: Prepare release assets run: | mkdir -p ${{ runner.temp }}/release-assets @@ -307,6 +324,12 @@ jobs: cp "${{ runner.temp }}/ios-build/ios-build-info.txt" "${{ runner.temp }}/release-assets/" fi + # Copy Helm chart package(s) + if compgen -G "${{ runner.temp }}/helm-artifacts/*.tgz" > /dev/null; then + cp ${{ runner.temp }}/helm-artifacts/*.tgz "${{ runner.temp }}/release-assets/" + echo "✓ Helm chart package prepared" + fi + echo "Release assets:" ls -la "${{ runner.temp }}/release-assets/" @@ -330,6 +353,38 @@ jobs: > **Note**: These are debug builds intended for testing purposes. For production use, please build from source with proper signing credentials. + + create_release_branch: + name: Create or update release branch + if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, 'alpha') && !contains(github.ref_name, 'beta') && !contains(github.ref_name, 'rc') + needs: [release] + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - name: Create/update minor release branch + env: + TAG_NAME: ${{ github.ref_name }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + + BRANCH_NAME="$(echo "$TAG_NAME" | sed -E 's/^v([0-9]+)\.([0-9]+)\..*/v\1.\2-release-branch/')" + SHA="${GITHUB_SHA}" + + echo "Updating ${BRANCH_NAME} -> ${SHA}" + if ! gh api "repos/${GITHUB_REPOSITORY}/git/refs/heads/${BRANCH_NAME}" \ + --method PATCH \ + --field sha="${SHA}" \ + --field force=true 2>/dev/null; then + gh api "repos/${GITHUB_REPOSITORY}/git/refs" \ + --method POST \ + --field ref="refs/heads/${BRANCH_NAME}" \ + --field sha="${SHA}" + fi + bump-pre_release-version: name: Bump Pre-release Version if: startsWith(github.ref, 'refs/tags/v') && (contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'rc'))