diff --git a/.github/actions/create-tag/action.yml b/.github/actions/create-tag/action.yml new file mode 100644 index 0000000..a7c1134 --- /dev/null +++ b/.github/actions/create-tag/action.yml @@ -0,0 +1,24 @@ +name: 'Create and Push Tag' +description: 'Creates an annotated tag and pushes it to the repository' +inputs: + tag: + description: 'Tag name to create' + required: true +runs: + using: 'composite' + steps: + - name: Check if tag exists + shell: bash + run: | + if git rev-parse "${{ inputs.tag }}" >/dev/null 2>&1; then + echo "Error: Tag ${{ inputs.tag }} already exists" + exit 1 + fi + + - name: Create and push tag + shell: bash + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag -a "${{ inputs.tag }}" -m "Release ${{ inputs.tag }}" + git push origin "${{ inputs.tag }}" diff --git a/.github/actions/normalize-version/action.yml b/.github/actions/normalize-version/action.yml new file mode 100644 index 0000000..0aebd8a --- /dev/null +++ b/.github/actions/normalize-version/action.yml @@ -0,0 +1,27 @@ +name: 'Normalize Version' +description: 'Normalizes a version string by removing v prefix and creating tag format' +inputs: + version: + description: 'Version string to normalize (e.g., 1.0.0 or v1.0.0)' + required: true +outputs: + version: + description: 'Normalized version without v prefix' + value: ${{ steps.normalize.outputs.version }} + tag: + description: 'Tag name with v prefix' + value: ${{ steps.normalize.outputs.tag }} +runs: + using: 'composite' + steps: + - name: Normalize version + id: normalize + shell: bash + run: | + version="${{ inputs.version }}" + # Remove 'v' prefix if present + version="${version#v}" + # Add 'v' prefix for tag + tag="v${version}" + echo "version=$version" >> $GITHUB_OUTPUT + echo "tag=$tag" >> $GITHUB_OUTPUT diff --git a/.github/workflows/ci_pipeline.yml b/.github/workflows/ci_pipeline.yml index 88caaf0..ca804e6 100644 --- a/.github/workflows/ci_pipeline.yml +++ b/.github/workflows/ci_pipeline.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: [ main ] + branches: [ main, bugfix/* ] pull_request: branches: [ main ] diff --git a/.github/workflows/prepare_bugfix.yml b/.github/workflows/prepare_bugfix.yml new file mode 100644 index 0000000..8ef23e6 --- /dev/null +++ b/.github/workflows/prepare_bugfix.yml @@ -0,0 +1,72 @@ +name: Prepare Bugfix Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Bugfix version (e.g., 1.0.1 or v1.0.1)' + required: true + type: string + +jobs: + create-bugfix-branch: + name: Create Bugfix Branch + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate not on main branch + run: | + current_branch="${{ github.ref_name }}" + if [ "$current_branch" = "main" ]; then + echo "Error: Cannot create bugfix branch from main branch. Please select another branch or a tag" + exit 1 + fi + echo "Creating bugfix branch from: $current_branch" + + - name: Normalize version and validate + id: normalize_version + run: | + version="${{ github.event.inputs.version }}" + # Remove 'v' prefix if present + version="${version#v}" + + # Validate semver format (basic check) + if ! echo "$version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$'; then + echo "Error: Invalid version format. Expected semver format (e.g., 1.0.1)" + exit 1 + fi + + # Extract patch version + patch_version=$(echo "$version" | cut -d. -f3 | cut -d- -f1) + + # Warn if patch version is 0 + if [ "$patch_version" = "0" ]; then + echo "::warning::Patch version is 0. Bugfix branches typically have a non-zero patch version (e.g., 1.0.1, not 1.0.0)" + fi + + branch_name="bugfix/$version" + echo "version=$version" >> $GITHUB_OUTPUT + echo "branch_name=$branch_name" >> $GITHUB_OUTPUT + + - name: Check if branch exists + run: | + if git ls-remote --heads origin "${{ steps.normalize_version.outputs.branch_name }}" | grep -q "${{ steps.normalize_version.outputs.branch_name }}"; then + echo "Error: Branch ${{ steps.normalize_version.outputs.branch_name }} already exists" + exit 1 + fi + + - name: Create and push bugfix branch + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout -b "${{ steps.normalize_version.outputs.branch_name }}" + git push -u origin "${{ steps.normalize_version.outputs.branch_name }}" + echo "✅ Created bugfix branch: ${{ steps.normalize_version.outputs.branch_name }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5472731 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +name: Create Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g., 1.0.0 or v1.0.0)' + required: true + type: string + +jobs: + create-tag: + name: Create Tag + runs-on: ubuntu-latest + + permissions: + contents: write + + outputs: + version: ${{ steps.normalize_version.outputs.version }} + tag: ${{ steps.normalize_version.outputs.tag }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Normalize version + id: normalize_version + uses: ./.github/actions/normalize-version + with: + version: ${{ github.event.inputs.version }} + + - name: Create and push tag + uses: ./.github/actions/create-tag + with: + tag: ${{ steps.normalize_version.outputs.tag }} \ No newline at end of file diff --git a/.github/workflows/release_bugfix.yml b/.github/workflows/release_bugfix.yml new file mode 100644 index 0000000..f065658 --- /dev/null +++ b/.github/workflows/release_bugfix.yml @@ -0,0 +1,110 @@ +name: Release Bugfix + +on: + workflow_dispatch: + +jobs: + create-tag: + name: Create Tag + runs-on: ubuntu-latest + + permissions: + contents: write + + outputs: + version: ${{ steps.extract_version.outputs.version }} + tag: ${{ steps.extract_version.outputs.tag }} + is_latest: ${{ steps.check_latest.outputs.is_latest }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate bugfix branch + run: | + current_branch="${{ github.ref_name }}" + if [[ ! "$current_branch" =~ ^bugfix/ ]]; then + echo "Error: This workflow must be run from a bugfix/* branch" + echo "Current branch: $current_branch" + exit 1 + fi + echo "Running on bugfix branch: $current_branch" + + - name: Extract version from branch name + id: extract_version + run: | + current_branch="${{ github.ref_name }}" + # Extract version from bugfix/X.Y.Z format + version="${current_branch#bugfix/}" + + # Validate semver format + if ! echo "$version" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$'; then + echo "Error: Invalid version format in branch name. Expected bugfix/X.Y.Z" + exit 1 + fi + + tag="v${version}" + echo "version=$version" >> $GITHUB_OUTPUT + echo "tag=$tag" >> $GITHUB_OUTPUT + echo "Extracted version: $version" + + - name: Check if this should be latest release + id: check_latest + run: | + current_version="${{ steps.extract_version.outputs.version }}" + + # Get all tags and find the highest version + highest_version=$(git tag -l 'v*' | sed 's/^v//' | sort -V | tail -n 1) + + if [ -z "$highest_version" ]; then + echo "No existing tags found, this will be latest" + echo "is_latest=true" >> $GITHUB_OUTPUT + else + echo "Current version: $current_version" + echo "Highest existing version: $highest_version" + + # Compare versions using sort -V + highest=$(printf "%s\n%s" "$current_version" "$highest_version" | sort -V | tail -n 1) + + if [ "$highest" = "$current_version" ]; then + echo "This version is higher than or equal to existing versions, marking as latest" + echo "is_latest=true" >> $GITHUB_OUTPUT + else + echo "This version is lower than existing versions, not marking as latest" + echo "is_latest=false" >> $GITHUB_OUTPUT + fi + fi + + - name: Create and push tag + uses: ./.github/actions/create-tag + with: + tag: ${{ steps.extract_version.outputs.tag }} + + create-release: + name: Create GitHub Release + needs: [ create-tag ] + runs-on: ubuntu-latest + + permissions: + contents: write + + steps: + - name: Create GitHub Release + uses: ncipollo/release-action@v1 + with: + tag: ${{ needs.create-tag.outputs.tag }} + name: Release ${{ needs.create-tag.outputs.tag }} + generateReleaseNotes: true + makeLatest: ${{ needs.create-tag.outputs.is_latest == 'true' }} + body: | + NuGet packages published to GitHub Packages. + + ### Installation + ```bash + dotnet add package DataPlane.Sdk.Core --version ${{ needs.create-tag.outputs.version }} + dotnet add package DataPlane.Sdk.Api --version ${{ needs.create-tag.outputs.version }} + ``` + draft: false + prerelease: false diff --git a/pkg/postgres/test_utils.go b/pkg/postgres/test_utils.go index 866b7d0..a3bc75f 100644 --- a/pkg/postgres/test_utils.go +++ b/pkg/postgres/test_utils.go @@ -7,6 +7,7 @@ import ( "fmt" "testing" + "github.com/docker/go-connections/nat" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" ) @@ -16,7 +17,12 @@ func setupTestContainer(t *testing.T) (testcontainers.Container, string) { req := testcontainers.ContainerRequest{ Image: "postgres:15-alpine", ExposedPorts: []string{"5432/tcp"}, - WaitingFor: wait.ForListeningPort("5432/tcp"), + WaitingFor: wait.ForAll( + wait.ForListeningPort("5432/tcp"), + wait.ForSQL("5432/tcp", "postgres", func(host string, port nat.Port) string { + return fmt.Sprintf("postgres://test:test@%s:%s/testdb?sslmode=disable", host, port.Port()) + }), + ), Env: map[string]string{ "POSTGRES_DB": "testdb", "POSTGRES_USER": "test",