Release Pipeline #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: "Release Pipeline" | |
on: | |
workflow_dispatch: | |
inputs: | |
branch: | |
description: The branch to create the release tag on | |
type: string | |
required: true | |
skip-test-checks: | |
description: Skip tests passed checks | |
type: boolean | |
default: false | |
required: false | |
skip-other-version-checks: | |
description: Skip server checks for core and frontend versions | |
type: boolean | |
default: false | |
required: false | |
permissions: | |
contents: write | |
jobs: | |
setup: | |
runs-on: ubuntu-latest | |
outputs: | |
constantsVersion: ${{ steps.versions.outputs.constantsVersion }} | |
constantsVersionXy: ${{ steps.versions.outputs.constantsVersionXy }} | |
setupVersion: ${{ steps.versions.outputs.setupVersion }} | |
setupVersionXy: ${{ steps.versions.outputs.setupVersionXy }} | |
newestVersion: ${{ steps.versions.outputs.newestVersion }} | |
targetBranch: ${{ steps.versions.outputs.targetBranch }} | |
devTag: ${{ steps.versions.outputs.devTag }} | |
releaseTag: ${{ steps.versions.outputs.releaseTag }} | |
versionFolder: ${{ steps.versions.outputs.versionFolder }} | |
artifactName: ${{ steps.versions.outputs.artifactName }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ inputs.branch }} | |
fetch-tags: true | |
token: ${{ secrets.ALL_REPO_PAT }} | |
- name: Populate variables | |
id: versions | |
run: | | |
. ./hooks/populate-hook-constants.sh | |
echo "constantsVersion=$constantsVersion" | tee -a "$GITHUB_OUTPUT" "$GITHUB_ENV" | |
echo "constantsVersionXy=$constantsVersionXy" | tee -a "$GITHUB_OUTPUT" "$GITHUB_ENV" | |
echo "setupVersion=$setupVersion" | tee -a "$GITHUB_OUTPUT" "$GITHUB_ENV" | |
echo "setupVersionXy=$setupVersionXy" | tee -a "$GITHUB_OUTPUT" "$GITHUB_ENV" | |
echo "newestVersion=$newestVersion" | tee -a "$GITHUB_OUTPUT" "$GITHUB_ENV" | |
echo "targetBranch=$targetBranch" | tee -a "$GITHUB_OUTPUT" "$GITHUB_ENV" | |
echo "devTag=dev-v$setupVersion" | tee -a "$GITHUB_OUTPUT" "$GITHUB_ENV" | |
echo "releaseTag=v$setupVersion" | tee -a "$GITHUB_OUTPUT" "$GITHUB_ENV" | |
echo "versionFolder=$setupVersionXy.X" | tee -a "$GITHUB_OUTPUT" "$GITHUB_ENV" | |
echo "artifactName=python-docs-$setupVersion" | tee -a "$GITHUB_OUTPUT" "$GITHUB_ENV" | |
mark-as-success: | |
runs-on: ubuntu-latest | |
needs: | |
- setup | |
steps: | |
- uses: actions/setup-python@v5 | |
with: | |
python-version: '3.13' | |
- name: Install dependencies | |
run: | | |
pip install httpx | |
- if: ${{ inputs.skip-test-checks == 'false' || inputs.skip-test-checks == false }} | |
name: Get commit status | |
run: | | |
python3 -c "$(cat << EOF | |
from collections import defaultdict | |
import httpx | |
import sys | |
check_runs_url = "https://api.github.com/repos/${{ github.repository }}/commits/tags/${{ needs.setup.outputs.devTag }}/check-runs?per_page=100&page={page}" | |
jobs_url="https://api.github.com/repos/supertokens/supertokens-python/actions/runs/${{ github.run_id }}/jobs" | |
current_jobs_response = httpx.get(jobs_url).json() | |
current_job_ids = [job["id"] for job in current_jobs_response["jobs"]] | |
page = 1 | |
total = 0 | |
status_map = defaultdict(int) | |
conclusion_map = defaultdict(int) | |
failures = [] | |
while True: | |
response = httpx.get(check_runs_url.format(page=page)).json() | |
if len(response["check_runs"]) == 0: | |
break | |
for run_info in response["check_runs"]: | |
# Release pipeline jobs also show up in check-runs | |
# We skip them from the checks to avoid pipeline failures | |
if run_info["id"] in current_job_ids: | |
continue | |
if run_info["conclusion"] == "failure": | |
failures.append(run_info["html_url"]) | |
status_map[run_info["status"]] += 1 | |
conclusion_map[run_info["conclusion"]] += 1 | |
total += 1 | |
page += 1 | |
print(f"{page=}") | |
print(f"{total=}") | |
print("Status Map =", dict(status_map)) | |
print("Conclusion Map =", dict(conclusion_map)) | |
print() | |
# Possible values (from docs): | |
# [completed, action_required, cancelled, failure, neutral, skipped, stale, success, | |
# timed_out, in_progress, queued, requested, waiting, pending] | |
if status_map["completed"] < total: | |
print("Some checks not completed.") | |
print(failures) | |
sys.exit(1) | |
# Possible values (from testing): | |
# None, success, skipped, failure | |
if conclusion_map.get("failure") > 0: | |
print("Some checks not successful.") | |
print(failures) | |
sys.exit(1) | |
EOF | |
)" | |
- run: | | |
curl --fail-with-body -X PATCH \ | |
https://api.supertokens.io/0/driver \ | |
-H 'Content-Type: application/json' \ | |
-H 'api-version: 0' \ | |
-d "{ | |
\"password\": \"${{ secrets.SUPERTOKENS_API_KEY }}\", | |
\"version\":\"${{ needs.setup.outputs.setupVersion }}\", | |
\"name\": \"python\", | |
\"testPassed\": true | |
}" | |
release: | |
runs-on: ubuntu-latest | |
# This job marks the version as a release version and creates tags. | |
# Binding this to the publish env to require approvals before run. | |
# Further jobs are follow-ups to the release and are not required to be approved once release is approved. | |
environment: publish | |
needs: | |
- setup | |
- mark-as-success | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ inputs.branch }} | |
fetch-tags: true | |
token: ${{ secrets.ALL_REPO_PAT }} | |
- name: Setup git | |
run: | | |
# NOTE: The user email is {user.id}+{user.login}@users.noreply.github.com. | |
# See users API: https://api.github.com/users/github-actions%5Bbot%5D | |
git config user.name "github-actions[bot]" | |
git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
- name: Check tests passed | |
run: | | |
testsPassed=`curl -s -X GET "https://api.supertokens.io/0/driver?password=${{ secrets.SUPERTOKENS_API_KEY }}&version=${{ needs.setup.outputs.setupVersion }}&name=python" -H 'api-version: 0'` | |
if [[ $(echo $testsPassed | jq .testPassed) != "true" ]] | |
then | |
echo "All tests have not passed. Exiting." | |
exit 1 | |
fi | |
- if: ${{ inputs.skip-other-version-checks == 'false' || inputs.skip-other-version-checks == false }} | |
name: Check if core and frontend released | |
run: | | |
canReleaseSafelyResponse=`curl -s -X GET "https://api.supertokens.io/0/driver/release/check?password=${{ secrets.SUPERTOKENS_API_KEY }}&version=${{ needs.setup.outputs.setupVersion }}&name=python" -H 'api-version: 0'` | |
if [[ $(echo $canReleaseSafelyResponse | jq .canRelease) != "true" ]] | |
then | |
echo "Cannot release. Have you released corresponding core and frontend?" | |
exit 1 | |
fi | |
- name: Check if current commit is dev-tagged | |
run: | | |
currentCommit=$(git log --format="%H" -n 1) | |
currentTag=`git tag -l --points-at $currentCommit` | |
expectedTag="${{ needs.setup.outputs.devTag }}" | |
if [[ $currentTag != $expectedTag ]] | |
then | |
echo "Commit does not have the correct dev tag for this release" | |
echo "Current: $currentTag" | |
echo "Expected: $expectedTag" | |
exit 1 | |
fi | |
- name: Mark for release | |
run: | | |
curl --fail-with-body -X PATCH \ | |
https://api.supertokens.io/0/driver \ | |
-H 'Content-Type: application/json' \ | |
-H 'api-version: 0' \ | |
-d "{ | |
\"password\": \"${{ secrets.SUPERTOKENS_RELEASE_API_KEY }}\", | |
\"name\":\"python\", | |
\"version\":\"${{ needs.setup.outputs.setupVersion }}\", | |
\"release\": true | |
}" | |
- name: Create release tag, delete dev tag | |
run: | | |
# Add new release tag | |
git tag ${{ needs.setup.outputs.releaseTag }} | |
git push --tags | |
# Delete current dev tag | |
git tag --delete ${{ needs.setup.outputs.devTag }} | |
git push --delete origin ${{ needs.setup.outputs.devTag }} | |
merge: | |
runs-on: ubuntu-latest | |
needs: | |
- setup | |
- release | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ inputs.branch }} | |
# Need a complete fetch to make the master merge work | |
fetch-depth: 0 | |
fetch-tags: true | |
token: ${{ secrets.ALL_REPO_PAT }} | |
- name: Setup git | |
run: | | |
# NOTE: The user email is {user.id}+{user.login}@users.noreply.github.com. | |
# See users API: https://api.github.com/users/github-actions%5Bbot%5D | |
git config user.name "github-actions[bot]" | |
git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
- name: Check API and merge to master | |
run: | | |
response=`curl -s -X GET "https://api.supertokens.io/0/driver/latest/check?password=${{ secrets.SUPERTOKENS_API_KEY }}&version=${{ needs.setup.outputs.setupVersion }}&name=python" -H 'api-version: 0'` | |
isLatest=$(echo $response | jq .isLatest) | |
if [[ $isLatest == "true" ]] | |
then | |
git checkout master | |
git checkout ${{ inputs.branch }} | |
git merge master | |
git checkout master | |
git merge ${{ inputs.branch }} | |
git push | |
git checkout ${{ inputs.branch }} | |
fi | |
publish-docs: | |
runs-on: ubuntu-latest | |
needs: | |
- setup | |
- release | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ needs.setup.outputs.releaseTag }} | |
fetch-tags: true | |
- uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ needs.setup.outputs.artifactName }} | |
path: html/supertokens_python | |
- name: Trigger the backend website CI | |
uses: actions/github-script@v7 | |
with: | |
# NOTE: We should use a better scoped PAT here. | |
github-token: ${{ secrets.ALL_REPO_PAT }} | |
script: | | |
github.rest.actions.createWorkflowDispatch({ | |
owner: 'supertokens', | |
repo: 'supertokens-backend-website', | |
workflow_id: 'release-python-documentation-changes.yml', | |
ref: 'master', | |
inputs: { | |
"version": `${{ needs.setup.outputs.setupVersion }}`, | |
"artifact-name": `${{ needs.setup.outputs.artifactName }}`, | |
"version-folder": `${{ needs.setup.outputs.versionFolder }}`, | |
"run-id": `${{ github.run_id }}`, | |
"stage": "production", | |
} | |
}) | |
publish: | |
runs-on: ubuntu-latest | |
needs: | |
- setup | |
- release | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: ${{ needs.setup.outputs.releaseTag }} | |
fetch-tags: true | |
- run: | | |
python3 -m pip install pip setuptools packaging poetry clikit --upgrade | |
make dev-install | |
pip install setuptools wheels twine | |
python setup.py sdist bdist_wheel | |
twine upload -u ${{ secrets.TWINE_USERNAME }} -p ${{ secrets.TWINE_PASSWORD }} dist/* |