Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
317 changes: 317 additions & 0 deletions .github/workflows/binary-prerelease.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
name: Build tccli binary pre-release

run-name: Build binaries for ${{ inputs.tag }}

on:
workflow_dispatch:
inputs:
tag:
description: "Existing version tag to build, for example v3.1.170.1rc2"
required: true
type: string
publish_release:
description: "Create a GitHub pre-release after all binary builds succeed"
required: true
default: false
type: boolean

permissions:
contents: read

concurrency:
group: binary-release-${{ github.repository }}-${{ inputs.tag }}
cancel-in-progress: false

jobs:
build:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: linux-x86_64
- os: macos-latest
target: darwin-arm64
- os: windows-latest
target: windows-x86_64
steps:
- name: Check out the requested tag
uses: actions/checkout@v4
with:
ref: ${{ inputs.tag }}
fetch-depth: 0
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Validate tag and package version
shell: bash
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail

if [[ ! "$RELEASE_TAG" =~ ^v[0-9][0-9A-Za-z.]*$ ]]; then
echo "tag must start with v and contain only version characters" >&2
exit 1
fi

git rev-parse --verify --quiet "refs/tags/${RELEASE_TAG}^{commit}" >/dev/null
source_sha="$(git rev-parse "${RELEASE_TAG}^{commit}")"
if [[ "$source_sha" != "$(git rev-parse HEAD)" ]]; then
echo "checked-out source does not match ${RELEASE_TAG}" >&2
exit 1
fi

release_version="${RELEASE_TAG#v}"
project_version="$(python - <<'PY'
from pathlib import Path
import tomllib

data = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
print(data["project"]["version"])
PY
)"

if [[ "$project_version" != "$release_version" ]]; then
echo "tag version ${release_version} does not match pyproject.toml version ${project_version}" >&2
exit 1
fi

echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
echo "SOURCE_SHA=${source_sha}" >> "$GITHUB_ENV"

- name: Install dependencies (lightweight common SDK for binary)
run: |
python -m pip install --upgrade pip
python -m pip install pyinstaller
python -m pip install tencentcloud-sdk-python-common jmespath six cos-python-sdk-v5
python -m pip install -e . --no-deps

- name: Run PyInstaller
if: runner.os != 'macOS'
run: >
python -m PyInstaller tccli/main.py -y -D
--collect-all tccli
--exclude-module tccli.examples
--name tccli

- name: Run PyInstaller on macOS
if: runner.os == 'macOS'
shell: bash
run: |
set -euo pipefail
python -m PyInstaller tccli/main.py -y -D \
--collect-all tccli \
--exclude-module tccli.examples \
--codesign-identity - \
--name tccli

- name: Re-sign and verify macOS bundle
if: runner.os == 'macOS'
shell: bash
run: |
set -euo pipefail
dist_dir="$GITHUB_WORKSPACE/dist/tccli"
main_binary="$dist_dir/tccli"
python_framework="$dist_dir/_internal/Python.framework"
python_library="$(find "$python_framework/Versions" -type f -name Python -print -quit)"

test -x "$main_binary"
test -d "$python_framework"
test -n "$python_library"
test -f "$python_library"

sign_macho() {
local binary="$1"
codesign --remove-signature "$binary" 2>/dev/null || true
codesign --force --sign - "$binary"
}

# Sign internal Mach-O files first, excluding the Python framework and main launcher.
while IFS= read -r -d '' binary; do
if [[ "$binary" == "$main_binary" || "$binary" == "$python_framework/"* ]]; then
continue
fi
if file -b "$binary" | grep -q 'Mach-O'; then
sign_macho "$binary"
fi
done < <(find "$dist_dir" -type f -print0)

# Sign Python.framework contents, then the framework bundle itself.
while IFS= read -r -d '' binary; do
if file -b "$binary" | grep -q 'Mach-O'; then
sign_macho "$binary"
fi
done < <(find "$python_framework" -type f -print0)
codesign --remove-signature "$python_framework" 2>/dev/null || true
codesign --force --sign - "$python_framework"

# Sign the launcher last, after all libraries it loads are finalized.
sign_macho "$main_binary"

codesign --verify --deep --strict --verbose=4 "$main_binary"
codesign --verify --strict --verbose=4 "$python_framework"
codesign --verify --strict --verbose=4 "$python_library"

- name: Smoke test binary on Linux or macOS
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
smoke_dir="$RUNNER_TEMP/tccli-smoke"
mkdir -p "$smoke_dir"
cd "$smoke_dir"
"$GITHUB_WORKSPACE/dist/tccli/tccli" --version
"$GITHUB_WORKSPACE/dist/tccli/tccli" cvm help

- name: Smoke test binary on Windows
if: runner.os == 'Windows'
shell: pwsh
run: |
$smokeDir = Join-Path $env:RUNNER_TEMP "tccli-smoke"
New-Item -ItemType Directory -Force -Path $smokeDir | Out-Null
Push-Location $smokeDir
try {
$binary = Join-Path $env:GITHUB_WORKSPACE "dist\tccli\tccli.exe"
& $binary --version
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
& $binary cvm help
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
}
finally {
Pop-Location
}

- name: Package binary for Linux or macOS
if: runner.os != 'Windows'
shell: bash
env:
TARGET: ${{ matrix.target }}
run: |
set -euo pipefail
mkdir -p release
tar -C dist -czf "release/tccli-${RELEASE_VERSION}-${TARGET}.tar.gz" tccli

- name: Package binary for Windows
if: runner.os == 'Windows'
shell: pwsh
env:
TARGET: ${{ matrix.target }}
run: |
New-Item -ItemType Directory -Force -Path release | Out-Null
$archive = "release\tccli-$env:RELEASE_VERSION-$env:TARGET.zip"
Compress-Archive -Path (Join-Path $PWD "dist\tccli") -DestinationPath $archive -Force

- name: Upload binary artifact
uses: actions/upload-artifact@v4
with:
name: tccli-${{ matrix.target }}
path: release/*
if-no-files-found: error
retention-days: 14

publish:
name: Publish GitHub pre-release
needs: build
if: ${{ inputs.publish_release }}
runs-on: ubuntu-latest
permissions:
actions: read
contents: write
steps:
- name: Check out the requested tag
uses: actions/checkout@v4
with:
ref: ${{ inputs.tag }}
fetch-depth: 0
persist-credentials: false

- name: Validate tag and package version
shell: bash
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail

if [[ ! "$RELEASE_TAG" =~ ^v[0-9][0-9A-Za-z.]*$ ]]; then
echo "tag must start with v and contain only version characters" >&2
exit 1
fi

git rev-parse --verify --quiet "refs/tags/${RELEASE_TAG}^{commit}" >/dev/null
source_sha="$(git rev-parse "${RELEASE_TAG}^{commit}")"
if [[ "$source_sha" != "$(git rev-parse HEAD)" ]]; then
echo "checked-out source does not match ${RELEASE_TAG}" >&2
exit 1
fi

release_version="${RELEASE_TAG#v}"
project_version="$(awk -F'"' '/^version[[:space:]]*=/ { print $2; exit }' pyproject.toml)"
if [[ "$project_version" != "$release_version" ]]; then
echo "tag version ${release_version} does not match pyproject.toml version ${project_version}" >&2
exit 1
fi

echo "RELEASE_VERSION=${release_version}" >> "$GITHUB_ENV"
echo "SOURCE_SHA=${source_sha}" >> "$GITHUB_ENV"

- name: Download platform artifacts
uses: actions/download-artifact@v4
with:
pattern: tccli-*
path: release
merge-multiple: true

- name: Generate release checksums
shell: bash
run: |
set -euo pipefail
shopt -s nullglob
assets=(release/tccli-"${RELEASE_VERSION}"-*)
if (( ${#assets[@]} == 0 )); then
echo "No binary artifacts were downloaded." >&2
exit 1
fi
for asset in "${assets[@]}"; do
test -f "$asset"
done
sha256sum "${assets[@]}" > release/SHA256SUMS.txt
cat release/SHA256SUMS.txt

- name: Create pre-release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag }}
REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail

if gh release view "$RELEASE_TAG" --repo "$REPOSITORY" >/dev/null 2>&1; then
echo "A release already exists for ${RELEASE_TAG}; refusing to replace published assets." >&2
exit 1
fi

{
printf 'Pre-release binary artifacts for `%s`.\n\n' "$RELEASE_TAG"
printf 'Source commit: `%s`\n' "$SOURCE_SHA"
} > release-notes.md

gh release create "$RELEASE_TAG" release/* \
--repo "$REPOSITORY" \
--verify-tag \
--prerelease \
--title "tccli ${RELEASE_VERSION}" \
--notes-file release-notes.md
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ exclude = ["tccli/examples"]

[project]
name = "tccli"
version = "3.1.142.1"
version = "3.1.171.1rc1"
authors = [
{ name="tencentcloudapi", email="tencentcloudapi@tencent.com" },
]
Expand All @@ -24,7 +24,7 @@ classifiers = [
dependencies = [
"jmespath>=0.10.0",
"six>=1.16.0",
"tencentcloud-sdk-python>=3.1.142",
"tencentcloud-sdk-python-common>=3.1.163",
"cos-python-sdk-v5>=1.9.0",
'futures>=3.2.0; python_version < "3.2"',
]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


def main():
dep_sdk = "tencentcloud-sdk-python >= %s" % __version__.rsplit(".", 1)[0]
dep_sdk = "tencentcloud-sdk-python-common == 3.1.163"
setup(
name='tccli',
install_requires=[
Expand Down
2 changes: 1 addition & 1 deletion tccli/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '3.1.142.1'
__version__ = '3.1.171.1rc1'
Loading