Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions .github/workflows/promote-artifact.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
name: Promote QA-approved artifacts to stable branch

# - Takes a commit SHA as input
# - Fast-forward merges it to ionos-stable (no new commits)
# - Promotes the exact artifact from Artifactory (no rebuild)

on:
workflow_dispatch:
inputs:
sha:
description: 'Commit SHA to promote from ionos-dev to ionos-stable and copy artifacts for'
required: true
type: string
env:
REGISTRY: ghcr.io
SHA: ${{ inputs.sha }}
ARTIFACTORY_REPOSITORY_SNAPSHOT: ionos-productivity-ncwserver-snapshot
ARTIFACTORY_REPOSITORY_RELEASE: ionos-productivity-ncwserver-release
CACHE_VERSION: v1.0
Comment on lines +15 to +19
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REGISTRY and CACHE_VERSION are defined in env: but are unused in this workflow. Removing unused env vars makes the workflow easier to understand and avoids confusion about whether registry/caching is involved in promotion.

Suggested change
REGISTRY: ghcr.io
SHA: ${{ inputs.sha }}
ARTIFACTORY_REPOSITORY_SNAPSHOT: ionos-productivity-ncwserver-snapshot
ARTIFACTORY_REPOSITORY_RELEASE: ionos-productivity-ncwserver-release
CACHE_VERSION: v1.0
SHA: ${{ inputs.sha }}
ARTIFACTORY_REPOSITORY_SNAPSHOT: ionos-productivity-ncwserver-snapshot
ARTIFACTORY_REPOSITORY_RELEASE: ionos-productivity-ncwserver-release

Copilot uses AI. Check for mistakes.

permissions:
contents: write

Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow performs stateful operations (pushing ionos-stable and copying to the release repo) but doesn’t define a concurrency group. Two manual promotions running at the same time can race (push conflicts, or copying different SHAs into the release location). Add a concurrency group (and usually cancel-in-progress: false) to serialize promotions.

Suggested change
concurrency:
group: promote-artifact
cancel-in-progress: false

Copilot uses AI. Check for mistakes.
jobs:
promote-git:
# Fast-forward merge SHA from ionos-dev into ionos-stable
# (This ensures commit-hash is identical)
Comment on lines +22 to +27
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

permissions: contents: write is granted at the workflow level, so both jobs (including the one that uses external secrets for Artifactory) receive a write-capable GITHUB_TOKEN. For least privilege, set workflow permissions to contents: read (or none) and override to contents: write only on the promote-git job that actually pushes.

Suggested change
contents: write
jobs:
promote-git:
# Fast-forward merge SHA from ionos-dev into ionos-stable
# (This ensures commit-hash is identical)
contents: read
jobs:
promote-git:
# Fast-forward merge SHA from ionos-dev into ionos-stable
# (This ensures commit-hash is identical)
permissions:
contents: write

Copilot uses AI. Check for mistakes.
runs-on: ubuntu-latest
steps:
- name: Checkout server
id: checkout_server
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Verify SHA is in ionos-dev
id: verify_sha
run: |
set -euo pipefail
git fetch origin ionos-dev ionos-stable
git checkout -B ionos-stable origin/ionos-stable
if ! git rev-parse --verify "$SHA^{commit}" >/dev/null 2>&1; then
echo "Error: SHA is not a valid commit: $SHA"
exit 1
fi
# verify SHA is on ionos-dev
if ! git merge-base --is-ancestor "$SHA" origin/ionos-dev; then
echo "Error: SHA does not exist on ionos-dev: $SHA"
exit 1
fi

- name: Fast-forward merge
id: ff_merge
run: |
set -euo pipefail
# Merge will fail if not possible
if ! git merge --ff-only "$SHA"; then
echo "Fast-forward merge not possible"
exit 1
else
git push origin HEAD:ionos-stable
fi

Comment on lines +60 to +63
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git merge --ff-only "$SHA" can exit successfully with "Already up to date." when the provided SHA is behind the current ionos-stable head (i.e., SHA is an ancestor of stable). In that case the branch is not moved to the requested SHA, but the workflow still proceeds to promote artifacts for it, breaking the "git SHA == promoted artifact" guarantee. Add an explicit check that origin/ionos-stable is an ancestor of $SHA before merging, or verify after the merge that HEAD equals $SHA and fail otherwise.

Suggested change
else
git push origin HEAD:ionos-stable
fi
fi
# Ensure that the resulting HEAD is exactly the requested SHA.
# This prevents the case where SHA is behind ionos-stable and
# 'git merge --ff-only' reports "Already up to date" without
# moving the branch to $SHA.
HEAD_SHA="$(git rev-parse HEAD)"
if [ "$HEAD_SHA" != "$SHA" ]; then
echo "Error: After merge, HEAD ($HEAD_SHA) does not match requested SHA ($SHA)."
echo "Refusing to push ionos-stable or promote artifacts for a mismatched commit."
exit 1
fi
git push origin HEAD:ionos-stable

Copilot uses AI. Check for mistakes.
promote-artifact:
# Copy specified artifact to the release repo -> No rebuild
# (ionos-productivity-ncwserver-release)
needs: promote-git
runs-on: ubuntu-latest
steps:
- name: Setup JFrog CLI
id: setup_jf
uses: jfrog/setup-jfrog-cli@7c95feb32008765e1b4e626b078dfd897c4340ad # v4.4.1
env:
JF_URL: ${{ secrets.JF_ARTIFACTORY_URL }}
JF_USER: ${{ secrets.JF_ARTIFACTORY_USER }}
JF_ACCESS_TOKEN: ${{ secrets.JF_ACCESS_TOKEN }}

- name: Ping the JF server
run: |
# Ping the server
jf rt ping

- name: Find artifacts matching the SHA
id: find_artifact
run: |
# Expected Artifactory layout:
# ionos-productivity-ncwserver-snapshot/
# dev/<branch-name>/$SHA/<artifact-files>
#
# We search for a single .tar.gz artifact for the given SHA and
# derive its containing directory. This avoids using wildcards
# for the branch component when copying to the release repo.
set -e
SEARCH_RESULT=$(jf rt search --format=json --limit=2 "${{ env.ARTIFACTORY_REPOSITORY_SNAPSHOT }}/dev/*/$SHA/*.tar.gz")
MATCH_COUNT=$(echo "$SEARCH_RESULT" | jq '.results | length')

if [ "$MATCH_COUNT" -eq 0 ]; then
echo "No artifact with SHA $SHA found."
exit 1
fi
Comment on lines +86 to +100
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Artifactory search path and expected file type don’t match the upload layout documented/implemented in build-artifact.yml (it uploads a .zip under .../dev/ncw-<version>/<shortSha>/..., not .tar.gz under .../dev/<branch>/$SHA/). As written, this step will not find any artifacts for valid builds. Align the search with the actual snapshot layout (or search by the stored vcs.revision property) and copy the exact matching build output.

Copilot uses AI. Check for mistakes.

if [ "$MATCH_COUNT" -gt 1 ]; then
echo "Multiple artifacts found for SHA $SHA; expected exactly one."
echo "$SEARCH_RESULT"
exit 1
fi

ARTIFACT_REPO=$(echo "$SEARCH_RESULT" | jq -r '.results[0].repo')
ARTIFACT_PATH=$(echo "$SEARCH_RESULT" | jq -r '.results[0].path')
# Derive the directory that contains all artifacts for this SHA
ARTIFACT_DIR_PATH=$(dirname "$ARTIFACT_PATH")
ARTIFACT_DIR="$ARTIFACT_REPO/$ARTIFACT_DIR_PATH"

echo "Using artifact directory: $ARTIFACT_DIR"
# Expose the directory as a step output for the copy step
echo "artifact_dir=$ARTIFACT_DIR" >> "$GITHUB_OUTPUT"
- name: Copy artifact to target
id: copy_artifact
run: |
jf rt copy \
"${{ steps.find_artifact.outputs.artifact_dir }}/*" \
"${{ env.ARTIFACTORY_REPOSITORY_RELEASE }}/stable/${SHA}/"

- name: Confirm promoted artifacts
run: |
jf rt search --format=table "${{ env.ARTIFACTORY_REPOSITORY_RELEASE }}/stable/${SHA}/*"
Loading