From c1b4b354e5b6ba9aa68638911f80e7fc7f82230a Mon Sep 17 00:00:00 2001 From: Peter Pathirana Date: Thu, 17 Sep 2026 00:43:49 +0000 Subject: [PATCH] ci(internal-workflows): make release publishing recoverable --- .github/workflows/publish.yaml | 31 ++++++++++++++++++------- actions/coder-template-push/action.yaml | 25 ++++++++++++++++++++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index a5a2259a..f0c77061 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -6,9 +6,15 @@ on: release: types: - published + workflow_dispatch: + inputs: + release_tag: + description: Existing release tag to publish. + required: true + type: string concurrency: - group: ${{ github.workflow }}-${{ github.event.release.tag_name }} + group: ${{ github.workflow }}-${{ github.event.release.tag_name || inputs.release_tag }} cancel-in-progress: true env: @@ -17,8 +23,8 @@ env: image_path: images/homelab-workspace template_path: templates/kubernetes/homelab-workspace template_name: homelab-workspace - source_ref: ${{ github.event.release.tag_name }} - source_sha: ${{ github.event.release.tag_name }} + source_ref: ${{ github.event.release.tag_name || inputs.release_tag }} + source_sha: ${{ github.event.release.tag_name || inputs.release_tag }} jobs: publish-image: @@ -33,7 +39,7 @@ jobs: platforms: linux/amd64,linux/arm64 private_registry_repository: ${{ vars.CONTAINER_REGISTRY_PATH }}/coder-workspace private_registry_build_cache: ${{ vars.CONTAINER_REGISTRY_CACHE_PATH }}/coder-workspace - git_ref: ${{ github.event.release.tag_name }} + git_ref: ${{ github.event.release.tag_name || inputs.release_tag }} ghcr_repository: ppat/coder-workspace timeout_minutes: 180 secrets: @@ -50,11 +56,20 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - name: Checkout + - name: Checkout publishing automation uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 1 persist-credentials: false + path: .automation + ref: main + + - name: Checkout release source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 1 + persist-credentials: false + path: release ref: ${{ env.source_ref }} - name: Tailscale Connect @@ -67,16 +82,16 @@ jobs: version: "1.102.4" - name: Login to Coder - uses: ./actions/coder-cli-login + uses: ./.automation/actions/coder-cli-login with: coder_url: ${{ secrets.CODER_URL }} coder_email: ${{ secrets.CODER_EMAIL }} coder_password: ${{ secrets.CODER_PASSWORD }} - name: Publish template - uses: ./actions/coder-template-push + uses: ./.automation/actions/coder-template-push with: - template_dir: ${{ env.template_path }} + template_dir: release/${{ env.template_path }} template_name: ${{ env.template_name }} template_version: ${{ env.source_sha }} workspace_image: "ghcr.io/ppat/coder-workspace:${{ needs.publish-image.outputs.image_tag }}" diff --git a/actions/coder-template-push/action.yaml b/actions/coder-template-push/action.yaml index 298ffad5..199bc501 100644 --- a/actions/coder-template-push/action.yaml +++ b/actions/coder-template-push/action.yaml @@ -54,6 +54,31 @@ runs: TEMPLATE_MESSAGE: ${{ inputs.message }} # yamllint disable-line rule:indentation run: | + existing_version="" + if coder templates list --output json | jq --exit-status --arg name "${TEMPLATE_NAME}" \ + 'any(.[]; .Template.name == $name)' >/dev/null; then + existing_version="$(coder templates versions list "${TEMPLATE_NAME}" --include-archived --output json | jq --compact-output \ + --arg name "${TEMPLATE_VERSION}" '.[] | select(.TemplateVersion.name == $name)')" + fi + if [[ -n "${existing_version}" ]]; then + existing_status="$(jq --raw-output '.TemplateVersion.job.status' <<<"${existing_version}")" + existing_active="$(jq --raw-output '.active' <<<"${existing_version}")" + existing_id="$(jq --raw-output '.TemplateVersion.id' <<<"${existing_version}")" + if [[ "${existing_status}" != "failed" || "${existing_active}" != "false" ]]; then + echo "::error::Template version ${TEMPLATE_VERSION} already exists with status=${existing_status}, active=${existing_active}; refusing to replace it." + exit 1 + fi + + coder_url="$(<"${HOME}/.config/coderv2/url")" + coder_session_token="$(<"${HOME}/.config/coderv2/session")" + failed_version_name="${TEMPLATE_VERSION}-failed-${existing_id%%-*}" + curl --fail --silent --show-error --request PATCH \ + --header "Coder-Session-Token: ${coder_session_token}" \ + --header "Content-Type: application/json" \ + --data "$(jq --null-input --arg name "${failed_version_name}" '{name: $name}')" \ + "${coder_url}/api/v2/templateversions/${existing_id}" >/dev/null + fi + variables_file="${RUNNER_TEMP}/coder-template-variables.yaml" jq --null-input \ --arg kubernetes_config_path "${KUBERNETES_CONFIG_PATH}" \