diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9bd0f85295..aa6b93b367 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -125,12 +125,11 @@ jobs: flavor: | latest=${{ (inputs.tag != '' && 'true') || 'auto' }} tags: | - type=semver,pattern={{version}},suffix=${{ inputs.build-os != '' && format('-{0}', inputs.build-os) || '' }} + type=semver,pattern={{version}},value=${{ inputs.tag }},enable=${{ inputs.tag != '' }},suffix=${{ inputs.build-os != '' && format('-{0}', inputs.build-os) || '' }} type=edge,suffix=${{ inputs.build-os != '' && format('-{0}', inputs.build-os) || '' }} type=schedule,suffix=${{ inputs.build-os != '' && format('-{0}', inputs.build-os) || '' }} type=ref,event=pr,suffix=${{ inputs.build-os != '' && format('-{0}', inputs.build-os) || '' }} type=ref,event=branch,suffix=-rc${{ inputs.build-os != '' && format('-{0}', inputs.build-os) || '' }},enable=${{ startsWith(github.ref, 'refs/heads/release') && inputs.tag == '' }} - type=raw,value=${{ inputs.tag }},enable=${{ inputs.tag != '' }},suffix=${{ inputs.build-os != '' && format('-{0}', inputs.build-os) || '' }} labels: | org.opencontainers.image.documentation=https://docs.nginx.com/nginx-gateway-fabric org.opencontainers.image.vendor=NGINX Inc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 698818bc88..5a75463e46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,21 +10,25 @@ on: - "**" schedule: - cron: "0 3 * * *" # run every day at 3am UTC (nightly builds) - workflow_call: + workflow_dispatch: inputs: is_production_release: + description: 'Is this a production release?' required: false type: boolean default: false release_version: + description: 'Release version (e.g., v2.0.3)' required: false type: string default: '' operator_version: + description: 'Operator release version (e.g., v1.0.0). Optional' required: false type: string default: '' dry_run: + description: 'If true, does a dry run of the production workflow' required: false type: boolean default: false @@ -34,13 +38,70 @@ defaults: shell: bash concurrency: - group: ${{ github.ref_name }}-ci - cancel-in-progress: true + group: ${{ inputs.is_production_release && format('prod-{0}', inputs.release_version) || format('{0}-ci', github.ref_name) }} + cancel-in-progress: ${{ !inputs.is_production_release }} permissions: contents: read jobs: + create-tag-and-release: + runs-on: ubuntu-24.04 + if: github.event_name == 'workflow_dispatch' && inputs.release_version != '' && startsWith(github.ref, 'refs/heads/release-') + permissions: + contents: write + steps: + - name: Validate Release Branch and Version + run: | + echo "Validating release from: ${GITHUB_REF}" + + INPUT_VERSION="${{ inputs.release_version }}" + INPUT_OPERATOR_VERSION="${{ inputs.operator_version }}" + + # Validate version format + if [[ ! "${INPUT_VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "❌ Invalid version format: ${INPUT_VERSION}" + echo "Expected format: v1.2.3" + exit 1 + fi + + # Validate version format if operator version is provided + if [[ -n "${INPUT_OPERATOR_VERSION}" && ! "${INPUT_OPERATOR_VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "❌ Invalid operator version format: ${INPUT_OPERATOR_VERSION}" + echo "Expected format: v1.2.3" + exit 1 + fi + + echo "✅ Valid release branch: ${GITHUB_REF}" + echo "✅ Valid version format: ${INPUT_VERSION}" + [[ -n "${INPUT_OPERATOR_VERSION}" ]] && echo "✅ Valid operator version format: ${INPUT_OPERATOR_VERSION}" + + - name: Checkout Repository + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + fetch-depth: 0 + + - name: Create Release Tag + run: | + VERSION="${{ inputs.release_version }}" + git config user.name "NGF Release Bot" + git config user.email "integrations@nginx.com" + + if git rev-parse --verify "refs/tags/${VERSION}" >/dev/null 2>&1; then + echo "Tag ${VERSION} already exists - skipping tag creation" + else + echo "Creating annotated tag ${VERSION}" + git tag -a "${VERSION}" -m "Release ${VERSION}" + + if [[ "${{ inputs.dry_run }}" == "true" ]]; then + echo "DRY RUN: Would push tag ${VERSION} and operator tag ${{ inputs.operator_version || '' }}" + git push --dry-run origin "${VERSION}" + else + git push origin "${VERSION}" + echo "Created and pushed tag: ${VERSION}" + fi + fi + vars: name: Checks and variables runs-on: ubuntu-24.04 diff --git a/.github/workflows/helm.yml b/.github/workflows/helm.yml index cf6ad99843..857207d4f7 100644 --- a/.github/workflows/helm.yml +++ b/.github/workflows/helm.yml @@ -44,12 +44,11 @@ jobs: images: | name=ghcr.io/nginx/nginx-gateway-fabric tags: | - type=semver,pattern={{version}} + type=semver,pattern={{version}},value=${{ inputs.tag }},enable=${{ inputs.tag != '' }} type=edge type=schedule type=ref,event=pr type=ref,event=branch,suffix=-rc,enable=${{ startsWith(github.ref, 'refs/heads/release') && inputs.tag == '' }} - type=raw,value=${{ inputs.tag }},enable=${{ inputs.tag != '' }} - name: NGINX Docker meta id: nginx-meta @@ -58,12 +57,11 @@ jobs: images: | name=ghcr.io/nginx/nginx-gateway-fabric/${{ inputs.image == 'plus' && 'nginx-plus' || inputs.image }} tags: | - type=semver,pattern={{version}} + type=semver,pattern={{version}},value=${{ inputs.tag }},enable=${{ inputs.tag != '' }} type=edge type=schedule type=ref,event=pr type=ref,event=branch,suffix=-rc,enable=${{ startsWith(github.ref, 'refs/heads/release') && inputs.tag == '' }} - type=raw,value=${{ inputs.tag }},enable=${{ inputs.tag != '' }} - name: Build NGF Docker Image uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 diff --git a/.github/workflows/production-release.yml b/.github/workflows/production-release.yml deleted file mode 100644 index 508cfebb2a..0000000000 --- a/.github/workflows/production-release.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Production Release - -on: - workflow_dispatch: - inputs: - version: - description: 'Release version (e.g., v2.0.3)' - required: true - type: string - operator-version: - description: 'Operator release version (e.g., v1.0.0). Optional' - required: false - type: string - default: '' - dry_run: - description: 'If true, does a dry run of the production workflow' - required: false - type: boolean - -run-name: ${{ inputs.dry_run && '[DRY RUN] ' || '' }}Release ${{ inputs.version }} by @${{ github.actor }} - -defaults: - run: - shell: bash - -permissions: - contents: read - -jobs: - create-tag-and-release: - runs-on: ubuntu-24.04 - if: startsWith(github.ref, 'refs/heads/release-') - permissions: - contents: write - steps: - - name: Validate Release Branch and Version - run: | - echo "Validating release from: ${GITHUB_REF}" - - INPUT_VERSION="${{ github.event.inputs.version }}" - INPUT_OPERATOR_VERSION="${{ github.event.inputs.operator-version }}" - - # Validate version format - if [[ ! "${INPUT_VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "❌ Invalid version format: ${INPUT_VERSION}" - echo "Expected format: v1.2.3" - exit 1 - fi - - # Validate version format if operator version is provided - if [[ -n "${INPUT_OPERATOR_VERSION}" && ! "${INPUT_OPERATOR_VERSION}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "❌ Invalid operator version format: ${INPUT_OPERATOR_VERSION}" - echo "Expected format: v1.2.3" - exit 1 - fi - - - echo "✅ Valid release branch: ${GITHUB_REF}" - echo "✅ Valid version format: ${INPUT_VERSION}" - [[ -n "${INPUT_OPERATOR_VERSION}" ]] && echo "✅ Valid operator version format: ${INPUT_OPERATOR_VERSION}" - - - name: Checkout Repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - fetch-depth: 0 - - - name: Create Release Tag - run: | - VERSION="${{ github.event.inputs.version }}" - git config user.name "NGF Release Bot" - git config user.email "integrations@nginx.com" - - if git rev-parse --verify "refs/tags/${VERSION}" >/dev/null 2>&1; then - echo "Tag ${VERSION} already exists - skipping tag creation" - else - echo "Creating annotated tag ${VERSION}" - git tag -a "${VERSION}" -m "Release ${VERSION}" - - if [[ "${{ inputs.dry_run }}" == "true" ]]; then - echo "DRY RUN: Would push tag ${VERSION} and operator tag ${{ github.event.inputs.operator-version || '' }}" - git push --dry-run origin "${VERSION}" - else - git push origin "${VERSION}" - echo "Created and pushed tag: ${VERSION}" - fi - fi - - production-build: - needs: create-tag-and-release - uses: ./.github/workflows/ci.yml - with: - is_production_release: true - release_version: ${{ github.event.inputs.version }} - operator_version: ${{ github.event.inputs.operator-version }} - dry_run: ${{ github.event.inputs.dry_run }} - secrets: inherit - permissions: - contents: write - packages: write - id-token: write - security-events: write - issues: write diff --git a/docs/developer/release-process.md b/docs/developer/release-process.md index 210417513b..a4f94455c6 100644 --- a/docs/developer/release-process.md +++ b/docs/developer/release-process.md @@ -57,8 +57,13 @@ To create a new release, follow these steps: created. If included, use the Release Notes specified in a PR. - If the supported Gateway API minor version has changed since the last release, add a note to the release notes explaining if the previous version is no longer supported. - Merge the release PR once it has received all necessary approvals. -6. Once you are ready to release, run the [Production Release](https://github.com/nginx/nginx-gateway-fabric/actions/workflows/production-release.yml) workflow with the correct tag e.g. `v2.1.0`. (Note: It is also possible to do a dry run of the production release workflow for verification if required. This will not push the tag, images, and chart, and won't publish the release) -If this release includes an updated release of our [Operator](https://github.com/nginx/nginx-gateway-fabric/tree/main/operators), include the new version as well e.g. `v1.0.1` +6. Once you are ready to release, trigger a production release by running the [CI workflow](https://github.com/nginx/nginx-gateway-fabric/actions/workflows/ci.yml) with the following inputs: + - Select the release branch (e.g., `release-2.2`) + - Set `is_production_release` to `true` (checked) + - Set `release_version` to the release tag (e.g., `v2.2.0`) + - If this release includes an updated release of our [Operator](https://github.com/nginx/nginx-gateway-fabric/tree/main/operators), set `operator_version` to the new version (e.g., `v1.0.1`) + - Set `dry_run` to `false` (unchecked) for a real release, or `true` for a dry run (Note: A dry run will not push the tag, images, and chart, and won't publish the release) + As a result, the CI/CD pipeline will: - Create and push the tag - Build NGF, NGINX and NGINX Plus container images with the release tag `X.Y.Z` and push them to the registries.