name: Create Release on: workflow_dispatch: inputs: tag: description: "Release tag (e.g. v1.83.0-stable)" required: true type: string commit_hash: description: "Full 40-char commit SHA to target" required: true type: string permissions: {} jobs: release: name: Create Release runs-on: ubuntu-latest permissions: contents: write steps: - name: Validate inputs env: TAG: ${{ inputs.tag }} COMMIT_HASH: ${{ inputs.commit_hash }} run: | if ! echo "${COMMIT_HASH}" | grep -qE '^[0-9a-f]{40}$'; then echo "::error::commit_hash must be a full 40-character commit SHA" exit 1 fi if ! echo "${TAG}" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+'; then echo "::error::tag must start with vX.Y.Z" exit 1 fi - name: Create release env: TAG: ${{ inputs.tag }} COMMIT_HASH: ${{ inputs.commit_hash }} uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | const tag = process.env.TAG; const commitHash = process.env.COMMIT_HASH; const cosignSection = [ `## Verify Docker Image Signature`, ``, `All LiteLLM Docker images are signed with [cosign](https://docs.sigstore.dev/cosign/overview/). Every release is signed with the same key introduced in [commit \`0112e53\`](https://github.com/BerriAI/litellm/commit/0112e53046018d726492c814b3644b7d376029d0).`, ``, `**Verify using the pinned commit hash (recommended):**`, ``, `A commit hash is cryptographically immutable, so this is the strongest way to ensure you are using the original signing key:`, ``, '```bash', `cosign verify \\`, ` --key https://raw.githubusercontent.com/BerriAI/litellm/0112e53046018d726492c814b3644b7d376029d0/cosign.pub \\`, ` ghcr.io/berriai/litellm:${tag}`, '```', ``, `**Verify using the release tag (convenience):**`, ``, `Tags are protected in this repository and resolve to the same key. This option is easier to read but relies on tag protection rules:`, ``, '```bash', `cosign verify \\`, ` --key https://raw.githubusercontent.com/BerriAI/litellm/${tag}/cosign.pub \\`, ` ghcr.io/berriai/litellm:${tag}`, '```', ``, `Expected output:`, ``, '```', `The following checks were performed on each of these signatures:`, ` - The cosign claims were validated`, ` - The signatures were verified against the specified public key`, '```', ``, `---`, ``, ].join('\n'); try { const response = await github.rest.repos.createRelease({ draft: true, generate_release_notes: true, target_commitish: commitHash, name: tag, owner: context.repo.owner, prerelease: false, repo: context.repo.repo, tag_name: tag, }); const updatedBody = cosignSection + (response.data.body ?? ''); await github.rest.repos.updateRelease({ owner: context.repo.owner, repo: context.repo.repo, release_id: response.data.id, body: updatedBody, draft: false, }); } catch (error) { core.setFailed(error.message); }