Skip to content
Merged
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
  •  
  •  
  •  
196 changes: 196 additions & 0 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
name: Build and Publish to PyPI

on:
push:
branches:
- test
- release
tags:
- '[0-9]+.[0-9]+.[0-9]+'
- '[0-9]+.[0-9]+.[0-9]+-*'
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
type: choice
options:
- test
- release

jobs:
build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
environment: ${{ steps.determine-env.outputs.environment }}

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Required for hatch-vcs versioning

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "pyproject.toml"

- name: Install build dependencies
run: |
uv pip install --system build hatch hatch-vcs twine

- name: Determine environment
id: determine-env
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == "refs/heads/test" ]]; then
echo "environment=test" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == "refs/heads/release" ]] || [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "environment=release" >> $GITHUB_OUTPUT
fi

- name: Get version and configure for environment
id: version
run: |
# Get version from git tags
BASE_VERSION=$(git describe --tags --always --match="*.*.*" | sed 's/^v//')
if [[ -z "$BASE_VERSION" || "$BASE_VERSION" == *"-"* ]]; then
# Fallback: use the latest tag or default
BASE_VERSION=$(git tag --sort=-version:refname | head -n1 | sed 's/^v//')
if [[ -z "$BASE_VERSION" ]]; then
BASE_VERSION="1.0.0rc4"
fi
fi
echo "Base version: $BASE_VERSION"

if [[ "${{ steps.determine-env.outputs.environment }}" == "test" ]]; then
# Test environment: append .devN
VERSION="${BASE_VERSION}.dev${{ github.run_number }}"
echo "Development version: $VERSION"
echo "SETUPTOOLS_SCM_PRETEND_VERSION=${VERSION}" >> $GITHUB_ENV
else
# Production environment: use version as-is
VERSION="${BASE_VERSION}"
echo "Production version: $VERSION"

# Verify no dev suffix for production
if [[ "$VERSION" =~ "dev" ]]; then
echo "Error: Version contains 'dev' suffix. Production releases must be clean versions."
echo "Current version: $VERSION"
echo "Please create a git tag (e.g., v1.0.0) on the release branch."
exit 1
fi
fi

echo "version=${VERSION}" >> $GITHUB_OUTPUT

- name: Build package
run: python -m build

- name: Check package
run: |
twine check dist/*
ls -lh dist/
echo "Package version: ${{ steps.version.outputs.version }}"

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dist-${{ steps.determine-env.outputs.environment }}
path: dist/

publish-test:
Comment on lines +23 to +110

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 3 months ago

To fix the problem, you should add an explicit permissions: block to the build job in the workflow YAML (.github/workflows/pypi.yml). This block should specify only the permissions needed by the build job. In practice, for jobs that just need access to the code (e.g., checkout), contents: read is sufficient.

  • Add the following beneath line 23 (runs-on: ubuntu-latest):

    permissions:
      contents: read

This change explicitly ensures that this job’s GITHUB_TOKEN can only read repository contents, and cannot write or modify anything, reducing the risk of privilege escalation or unintended actions.

Suggested changeset 1
.github/workflows/pypi.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml
--- a/.github/workflows/pypi.yml
+++ b/.github/workflows/pypi.yml
@@ -21,6 +21,8 @@
 jobs:
   build:
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
     outputs:
       version: ${{ steps.version.outputs.version }}
       environment: ${{ steps.determine-env.outputs.environment }}
EOF
@@ -21,6 +21,8 @@
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
version: ${{ steps.version.outputs.version }}
environment: ${{ steps.determine-env.outputs.environment }}
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
needs: build
if: needs.build.outputs.environment == 'test'
runs-on: ubuntu-latest
environment:
name: test
url: https://test.pypi.org/project/workato-platform-cli/

permissions:
id-token: write # Required for trusted publishing

steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist-test
path: dist/

- name: Publish to Test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
skip-existing: true
print-hash: true

- name: Set up Python for testing
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
cache-dependency-glob: "pyproject.toml"

- name: Test installation
run: |
echo "Waiting for Test PyPI to process the package..."
sleep 30

echo "Attempting to install latest workato-platform-cli from TestPyPI"
uv pip install --system \
--index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
workato-platform-cli || echo "Package not yet available on Test PyPI"

# Verify CLI if installation succeeded
workato --version || echo "CLI check skipped"

publish-release:
needs: build
if: needs.build.outputs.environment == 'release'
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/project/workato-platform-cli/

permissions:
id-token: write # Required for trusted publishing
contents: write # For creating GitHub releases

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist-release
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
print-hash: true

- name: Create GitHub Release
if: startsWith(github.ref, 'refs/tags/')
uses: softprops/action-gh-release@v1
with:
files: dist/*
generate_release_notes: true
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ test-integration:
uv run pytest tests/integration/ -v

test-client:
uv run pytest src/workato_platform/client/workato_api/test/ -v
uv run pytest src/workato_platform_cli/client/workato_api/test/ -v

test-cov:
uv run pytest tests/ --cov=src/workato_platform --cov-report=html --cov-report=term --cov-report=xml
uv run pytest tests/ --cov=src/workato_platform_cli --cov-report=html --cov-report=term --cov-report=xml

test-watch:
uv run pytest tests/ -v --tb=short -x --lf
Expand Down
2 changes: 1 addition & 1 deletion openapi-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
library: asyncio
packageName: workato_platform.client.workato_api
packageName: workato_platform_cli.client.workato_api
projectName: workato-platform-cli
packageVersion: 1.0.0
packageUrl: https://github.com/workato/workato-platform-cli
Expand Down
38 changes: 19 additions & 19 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ readme = "README.md"
requires-python = ">=3.11"
license = { text = "MIT" }
authors = [
{ name = "Workato CLI Team" },
{ name = "Workato CLI Team", email = "workato-devs@workato.com" },
]
keywords = ["workato", "cli", "automation", "api"]
classifiers = [
Expand Down Expand Up @@ -63,15 +63,15 @@ test = [
]

[project.scripts]
workato = "workato_platform.cli:cli"
workato = "workato_platform_cli.cli:cli"

[project.urls]
Homepage = "https://github.com/workato/workato-platform-cli"
Repository = "https://github.com/workato/workato-platform-cli.git"
Issues = "https://github.com/workato/workato-platform-cli/issues"
Homepage = "https://github.com/workato-devs/workato-platform-cli"
Repository = "https://github.com/workato-devs/workato-platform-cli.git"
Issues = "https://github.com/workato-devs/workato-platform-cli/issues"

[tool.hatch.build.targets.wheel]
packages = ["src/workato_platform"]
packages = ["src/workato_platform_cli"]

[tool.hatch.build.targets.wheel.sources]
"src" = ""
Expand Down Expand Up @@ -100,7 +100,7 @@ exclude = [
"dist",
"node_modules",
"venv",
"src/workato_platform/client/",
"src/workato_platform_cli/client/",
]

[tool.ruff.lint]
Expand All @@ -123,14 +123,14 @@ ignore = [

[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["B011", "S101", "S105", "S106"]
"src/workato_platform/_version.py" = ["ALL"]
"src/workato_platform_cli/_version.py" = ["ALL"]

# Ruff isort configuration
[tool.ruff.lint.isort]
force-single-line = false
lines-between-types = 1
lines-after-imports = 2
known-first-party = ["workato_platform"]
known-first-party = ["workato_platform_cli"]
known-third-party = ["workato_api"]
section-order = ["future", "standard-library", "third-party", "first-party", "local-folder"]

Expand All @@ -141,8 +141,8 @@ indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"
exclude = [
"src/workato_platform/_version.py",
"src/workato_platform/client/"
"src/workato_platform_cli/_version.py",
"src/workato_platform_cli/client/"
]

# MyPy configuration
Expand All @@ -164,16 +164,16 @@ namespace_packages = true
explicit_package_bases = true
mypy_path = "src"
files = [
"src/workato_platform",
"src/workato_platform_cli",
"tests",
]
plugins = ["pydantic.mypy"]
exclude = [
"src/workato_platform/client/*",
"src/workato_platform_cli/client/*",
]

[[tool.mypy.overrides]]
module = "workato_platform.client.workato_api.*"
module = "workato_platform_cli.client.workato_api.*"
ignore_errors = true

[[tool.mypy.overrides]]
Expand Down Expand Up @@ -203,11 +203,11 @@ pythonpath = ["src"]

# Coverage configuration
[tool.coverage.run]
source = ["src/workato_platform"]
source = ["src/workato_platform_cli"]
omit = [
"tests/*",
"src/workato_platform/client/*",
"src/workato_platform/_version.py",
"src/workato_platform_cli/client/*",
"src/workato_platform_cli/_version.py",
]

[tool.coverage.report]
Expand All @@ -226,10 +226,10 @@ exclude_lines = [

[tool.hatch.version]
source = "vcs"
raw-options = { local_scheme = "no-local-version" }

[tool.hatch.build.hooks.vcs]
version-file = "src/workato_platform/_version.py"

version-file = "src/workato_platform_cli/_version.py"

[dependency-groups]
dev = [
Expand Down
Loading
Loading