diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 03d30b6..0baf444 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,6 +25,7 @@ repos: - id: check-case-conflict - id: check-executables-have-shebangs - id: check-yaml + exclude: 'adbc_drivers_dev/templates/.*\.yaml' - id: detect-private-key - id: end-of-file-fixer - id: mixed-line-ending diff --git a/MANIFEST.in b/MANIFEST.in index 6b93e35..c3e566a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -15,3 +15,4 @@ include adbc_drivers_dev/.env include adbc_drivers_dev/compose.yaml recursive-include adbc_drivers_dev/compose/ Dockerfile *.ld *.sh +recursive-include adbc_drivers_dev/templates/ *.yaml diff --git a/adbc_drivers_dev/templates/dev.yaml b/adbc_drivers_dev/templates/dev.yaml new file mode 100644 index 0000000..a7cd952 --- /dev/null +++ b/adbc_drivers_dev/templates/dev.yaml @@ -0,0 +1,75 @@ +<% if private %> +# Copyright (c) 2025 Columnar Technologies Inc. All rights reserved. +<% else %> +# Copyright (c) 2025 ADBC Drivers Contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +<% endif %> + +# This is a common workflow for performing basic PR checks like running +# pre-commit. + +name: Dev + +on: + pull_request: {} + push: + branches: + - main + +concurrency: + group: ${{ github.repository }}-${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +permissions: + contents: read + +jobs: + lint: + name: "lint & pre-commit" + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + +<% if lang.get("go") %> + - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 + with: + cache-dependency-path: go/go.sum + check-latest: true + go-version-file: go/go.mod +<% endif %> + + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "3.x" + + - name: Install + run: pip install pre-commit + + - name: pre-commit (cache) + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} + + - name: pre-commit + run: | + pre-commit run --all-files --color=always --show-diff-on-failure --verbose diff --git a/adbc_drivers_dev/templates/dev_issues.yaml b/adbc_drivers_dev/templates/dev_issues.yaml new file mode 100644 index 0000000..e5ca496 --- /dev/null +++ b/adbc_drivers_dev/templates/dev_issues.yaml @@ -0,0 +1,47 @@ +<% if private %> +# Copyright (c) 2025 Columnar Technologies Inc. All rights reserved. +<% else %> +# Copyright (c) 2025 ADBC Drivers Contributors +<% endif %> +# +# This file has been modified from its original version, which is +# under the Apache License: +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Issues Bot + +on: + issue_comment: + types: + - created + - edited + +jobs: + issue_assign: + name: "Assign issue" + permissions: + issues: write + if: github.event.comment.body == 'take' + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + assignees: context.payload.comment.user.login + }); diff --git a/adbc_drivers_dev/templates/dev_pr.yaml b/adbc_drivers_dev/templates/dev_pr.yaml new file mode 100644 index 0000000..2686e94 --- /dev/null +++ b/adbc_drivers_dev/templates/dev_pr.yaml @@ -0,0 +1,71 @@ +<% if private %> +# Copyright (c) 2025 Columnar Technologies Inc. All rights reserved. +<% else %> +# Copyright (c) 2025 ADBC Drivers Contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +<% endif %> + +# This is a common workflow for performing basic PR checks, like ensuring the +# title format. + +name: Dev PR + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + - ready_for_review + - review_requested + +concurrency: + group: ${{ github.repository }}-${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +permissions: + contents: read + +jobs: + pr_standard: + name: "Check PR" + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: +<% if driver == "dev" %> + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 1 + persist-credentials: false + + - name: Check PR title format + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + python adbc_drivers_dev/title_check.py $(pwd) "$PR_TITLE" +<% else %> + - name: Check PR title format + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + git clone --depth 1 https://github.com/adbc-drivers/dev + python dev/adbc_drivers_dev/title_check.py $(pwd) "$PR_TITLE" +<% endif %> + + # TODO: assign milestone diff --git a/adbc_drivers_dev/templates/test.yaml b/adbc_drivers_dev/templates/test.yaml new file mode 100644 index 0000000..7476d8a --- /dev/null +++ b/adbc_drivers_dev/templates/test.yaml @@ -0,0 +1,508 @@ +<% if private %> +# Copyright (c) 2025 Columnar Technologies Inc. All rights reserved. +<% else %> +# Copyright (c) 2025 ADBC Drivers Contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +<% endif %> + +# !!!! AUTO-GENERATED FILE. DO NOT EDIT. !!!! +# USE adbc-gen-workflow (see adbc-drivers/dev) TO UPDATE THIS FILE. + +# This is a common workflow for building & testing Go drivers: +# +# - Build the driver +# - Start dependencies +# - Run tests +# - Build the shared library + +name: Go <{workflow_name}> + +on: + pull_request: + branches: + - main + paths: +<% if not release %> + - "go/**" +<% endif %> +<% for path in pull_request_trigger_paths %> + - <{path}> +<% endfor %> + + push: +<% if release %> + tags: + - "go/v**" +<% else %> + branches: + - main + paths: + - "go/**" +<% for path in pull_request_trigger_paths %> + - <{path}> +<% endfor %> +<% endif %> + + workflow_dispatch: {} + + +concurrency: + # Must share concurrency group with release workflow since it also builds/tests + group: ${{ github.repository }}-${{ github.ref }}-ci + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + test: + name: "Test/${{ matrix.os }} ${{ matrix.arch }}" + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: true + matrix: + include: + - { os: Linux, platform: linux, arch: amd64, runner: ubuntu-latest } + - { os: macOS, platform: macos, arch: arm64, runner: macos-latest } + - { os: Windows, platform: win, arch: amd64, runner: windows-latest } +<% if environment %> + environment: <{environment}> +<% endif %> + permissions: + contents: read +<% if permissions.get("id_token") %> + id-token: write +<% endif %> + steps: + - name: free up disk space + if: runner.os != 'Windows' + run: | + # Rust uses a lot of disk space, free up some space + # https://github.com/actions/runner-images/issues/2840 + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 + with: + cache-dependency-path: go/go.sum + check-latest: true + go-version-file: go/go.mod + + - uses: prefix-dev/setup-pixi@82d477f15f3a381dbcc8adc1206ce643fe110fb7 # v0.9.3 + with: + pixi-version: v0.50.2 + run-install: false + +<% if aws %> + - name: AWS Login + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 + with: + aws-region: <{aws.region}> + role-to-assume: ${{ secrets.aws_role }} + role-session-name: ${{ secrets.aws_role_session_name }} +<% endif %> +<% if gcloud %> + - name: Google Cloud Login + uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 + with: + service_account: ${{ secrets.gcloud_service_account }} + workload_identity_provider: ${{ secrets.gcloud_workload_identity_provider }} +<% endif %> + + - name: Build + working-directory: go + run: | + go build ./... + + - name: Start Test Dependencies + # Can't use Docker on macOS AArch64 runners, and Windows containers + # work but often the container doesn't support Windows + if: runner.os == 'Linux' + working-directory: go + run: | + if [[ -f compose.yaml ]]; then + if ! docker compose up --detach --wait test-service; then + echo "Service failed to start" + echo "Logs:" + docker compose logs test-service + exit 1 + fi + fi + + - name: Test + if: runner.os == 'Linux' + working-directory: go + run: | + set -a + if [[ -f .env ]]; then + source .env + fi + if [[ -f .env.ci ]]; then + source .env.ci + fi + set +a + + if [[ -n "${{ secrets.environment }}" ]]; then + echo "Loading secret environment variables" + eval "${{ secrets.environment }}" + fi + + if [[ -f ci/scripts/pre-test.sh ]]; then + echo "Loading pre-test" + ./ci/scripts/pre-test.sh + fi + + go test -tags assert -v ./... + + if [[ -f ci/scripts/post-test.sh ]]; then + ./ci/scripts/post-test.sh + fi + + - name: go mod tidy + if: runner.os == 'Linux' + working-directory: go + run: | + go mod tidy --diff + + - name: Test + if: runner.os != 'Linux' + working-directory: go + run: | + go test -tags assert -v ./... + + validate: + name: "Validate/${{ matrix.os }} ${{ matrix.arch }}" + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: true + matrix: + include: + # I think we only need to test one platform, but we can change that later + - { os: Linux, platform: linux, arch: amd64, runner: ubuntu-latest } +<% if environment %> + environment: <{environment}> +<% endif %> + permissions: + contents: read +<% if permissions.get("id_token") %> + id-token: write +<% endif %> + steps: + - name: free up disk space + if: runner.os != 'Windows' + run: | + # Rust uses a lot of disk space, free up some space + # https://github.com/actions/runner-images/issues/2840 + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 + with: + cache-dependency-path: go/go.sum + check-latest: true + go-version-file: go/go.mod + + - uses: prefix-dev/setup-pixi@82d477f15f3a381dbcc8adc1206ce643fe110fb7 # v0.9.3 + with: + pixi-version: v0.50.2 + run-install: false + +<% if aws %> + - name: AWS Login + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 + with: + aws-region: <{aws.region}> + role-to-assume: ${{ secrets.aws_role }} + role-session-name: ${{ secrets.aws_role_session_name }} +<% endif %> +<% if gcloud %> + - name: Google Cloud Login + uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0 + with: + service_account: ${{ secrets.gcloud_service_account }} + workload_identity_provider: ${{ secrets.gcloud_workload_identity_provider }} +<% endif %> + + - name: Build Library + working-directory: go + run: | + pixi run adbc-make build DEBUG=true VERBOSE=true DRIVER=<{driver}> + + - name: Start Test Dependencies + # Can't use Docker on macOS AArch64 runners, and Windows containers + # work but often the container doesn't support Windows + if: runner.os == 'Linux' + working-directory: go + run: | + if [[ -f compose.yaml ]]; then + if ! docker compose up --detach --wait test-service; then + echo "Service failed to start" + echo "Logs:" + docker compose logs test-service + exit 1 + fi + fi + + - name: Validate + if: runner.os == 'Linux' + working-directory: go + run: | + set -a + if [[ -f .env ]]; then + source .env + fi + if [[ -f .env.ci ]]; then + source .env.ci + fi + set +a + + if [[ -n "${{ secrets.environment }}" ]]; then + echo "Loading secret environment variables" + eval "${{ secrets.environment }}" + fi + + if [[ -f ci/scripts/pre-test.sh ]]; then + echo "Loading pre-test" + ./ci/scripts/pre-test.sh + fi + + docker ps + pixi run validate + + if [[ -f ci/scripts/post-test.sh ]]; then + ./ci/scripts/post-test.sh + fi + + - name: Generate docs + working-directory: go + run: | + pixi run gendocs --output generated + + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: docs + path: "go/generated/<{driver}>.md" + retention-days: 2 + + build: + name: "Build <{driver}>/${{ matrix.os }} ${{ matrix.arch }}" + needs: test + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: true + matrix: + include: + - { os: Linux, platform: linux, arch: amd64, runner: ubuntu-latest } +<% if private %> + - { os: Linux, platform: linux, arch: arm64, runner: private-ubuntu-24.04-arm } +<% else %> + - { os: Linux, platform: linux, arch: arm64, runner: ubuntu-24.04-arm } +<% endif %> + - { os: macOS, platform: macos, arch: arm64, runner: macos-latest } + - { os: Windows, platform: windows, arch: amd64, runner: windows-latest } + permissions: + contents: read + packages: read + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 + with: + cache-dependency-path: go/go.sum + check-latest: true + go-version-file: go/go.mod + + - uses: prefix-dev/setup-pixi@82d477f15f3a381dbcc8adc1206ce643fe110fb7 # v0.9.3 + with: + pixi-version: v0.50.2 + run-install: false + + - name: Install dev tools + working-directory: go + run: | + pixi install + + - name: Log in to ghcr.io + if: runner.os == 'Linux' + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + + - name: Build Library + working-directory: go + run: | + pixi run adbc-make check CI=true VERBOSE=true DRIVER=<{driver}> + + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: drivers-${{ matrix.platform }}-${{ matrix.arch }} + path: "go/build/libadbc_driver_<{driver}>.*" + retention-days: 2 + + package: + name: "Generate Packages" + runs-on: ubuntu-latest + needs: build + permissions: + contents: read + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 + with: + check-latest: true + go-version: "stable" + + - uses: prefix-dev/setup-pixi@82d477f15f3a381dbcc8adc1206ce643fe110fb7 # v0.9.3 + with: + pixi-version: v0.50.2 + run-install: false + + - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + pattern: "drivers-*" + path: "~/drivers" + + - name: Install tools + working-directory: go + run: | + # XXX: can't install go-licenses under go 1.25 + # https://github.com/google/go-licenses/issues/312 + git clone --depth=1 https://github.com/google/go-licenses + git config --global --add 'url.https://github.com/.insteadOf' ssh://git@github.com/ + pushd go-licenses + go get -u + # Silent break: https://github.com/spf13/cobra/pull/2303 + # Manually edit the go.mod to get around this for now + sed -i 's|github.com/spf13/pflag v1.0.8|github.com/spf13/pflag v1.0.7|g' go.mod + go mod tidy + go install . + popd + + - name: Generate packages + working-directory: go + run: | + pixi install + + pixi run adbc-gen-package \ + --name <{driver}> \ + --root $(pwd) \ + --manifest-template manifest.toml \ + ${{ (inputs.release && '--release') || '' }}\ + -o ~/packages \ + ~/drivers/drivers-*-*/ + + ls ~/packages + + - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: all-packages + path: ~/packages + retention-days: 7 + + release: +<% if release %> + name: "Release" +<% else %> + name: "Release (Dry Run)" +<% endif %> + runs-on: ubuntu-latest + needs: + - package + - validate + permissions: +<% if release %> + contents: write +<% else %> + contents: read +<% endif %> + + steps: + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 + with: + check-latest: true + go-version: "stable" + + - uses: prefix-dev/setup-pixi@82d477f15f3a381dbcc8adc1206ce643fe110fb7 # v0.9.3 + with: + pixi-version: v0.50.2 + run-install: false + + - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + name: "all-packages" + path: "~/packages" + + - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 + with: + name: "docs" + path: "~/packages" + +<% if release %> + - name: Release + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + working-directory: go + run: | + ref=${{ github.ref }} + tag=${ref#"refs/tags/"} + + pixi run release $(pwd) $tag + gh release upload $tag $(find ~/packages -name '*.tar.gz') $(find ~/packages -name 'manifest.yaml') $(find ~/packages -name '*.md') + + - name: Release (dry-run) + if: github.event_name != 'push' || !startsWith(github.ref, 'refs/tags/') + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + working-directory: go + run: | + git tag go/v1000.0.0 + tag=go/v1000.0.0 + + pixi run release --dry-run $(pwd) $tag + echo gh release upload $tag $(find ~/packages -name '*.tar.gz') $(find ~/packages -name 'manifest.yaml') $(find ~/packages -name '*.md') +<% else %> + - name: Release (dry-run) + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + working-directory: go + run: | + git tag go/v1000.0.0 + tag=go/v1000.0.0 + + pixi run release --dry-run $(pwd) $tag + echo gh release upload $tag $(find ~/packages -name '*.tar.gz') $(find ~/packages -name 'manifest.yaml') $(find ~/packages -name '*.md') +<% endif %> diff --git a/adbc_drivers_dev/workflow.py b/adbc_drivers_dev/workflow.py new file mode 100644 index 0000000..74d981c --- /dev/null +++ b/adbc_drivers_dev/workflow.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python3 +# Copyright (c) 2025 ADBC Drivers Contributors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generate workflows for a particular repository.""" + +import argparse +import functools +import itertools +import re +import subprocess +import sys +import typing +from pathlib import Path + +import jinja2 +import packaging.version +import tomlkit + +DEFAULT_PARAMS = { + "driver": "(unknown)", + "private": False, + "lang": {}, + "permissions": {}, + "aws": {}, + "gcloud": {}, +} + +# TOML does not support nulls +MORE_DEFAULTS = { + "environment": None, +} + + +def write_workflow( + root: Path, template, filename: str, params: dict[str, typing.Any] +) -> None: + rendered = template.render(**params) + sink = root / ".github/workflows" / filename + with sink.open("w") as f: + f.write(rendered) + if not rendered.endswith("\n"): + f.write("\n") + print("Wrote", sink) + + +def generate_workflows(args) -> int: + env = jinja2.Environment( + loader=jinja2.PackageLoader("adbc_drivers_dev"), + autoescape=jinja2.select_autoescape(), + block_start_string="<%", + block_end_string="%>", + variable_start_string="<{", + variable_end_string="}>", + trim_blocks=True, + undefined=jinja2.StrictUndefined, + ) + + config_path = args.repository / ".github/workflows/generate.toml" + try: + with config_path.open("rb") as f: + params = tomlkit.load(f).unwrap() + except FileNotFoundError: + print(f"{config_path} not found.", file=sys.stderr) + + config_path.parent.mkdir(parents=True, exist_ok=True) + with config_path.open("w") as f: + tomlkit.dump(DEFAULT_PARAMS, f) + print("Wrote out defaults, please fill it in.", file=sys.stderr) + + return 1 + + for key, value in itertools.chain(DEFAULT_PARAMS.items(), MORE_DEFAULTS.items()): + if key not in params: + params[key] = value + + if params["aws"] or params["gcloud"]: + params["permissions"]["id_token"] = True + + template = env.get_template("test.yaml") + write_workflow( + args.repository, + template, + "go_test.yaml", + { + **params, + "pull_request_trigger_paths": [".github/workflows/go_test.yaml"], + "release": False, + "workflow_name": "Test", + }, + ) + write_workflow( + args.repository, + template, + "go_release.yaml", + { + **params, + "pull_request_trigger_paths": [".github/workflows/go_release.yaml"], + "release": True, + "workflow_name": "Release", + }, + ) + + for dev in ["dev.yaml", "dev_issues.yaml", "dev_pr.yaml"]: + template = env.get_template(dev) + write_workflow( + args.repository, + template, + dev, + { + **params, + }, + ) + + return 0 + + +@functools.cache +def latest_action_version(action: str) -> (packaging.version.Version, str, str): + # XXX: this won't work with repos that have multiple actions + result = subprocess.check_output( + [ + "git", + "ls-remote", + "--refs", + "--tags", + "--exit-code", + "--quiet", + f"https://github.com/{action}", + ], + text=True, + ) + tags = [] + for line in result.strip().splitlines(): + sha, ref = line.split() + tag = ref.removeprefix("refs/tags/") + + if tag == "master" or "-node" in tag or tag == "testEnableForGHES": + # aws-actions/configure-aws-credentials, others have weird tags + continue + + version = packaging.version.parse(tag.lstrip("v")) + tags.append((version, tag, sha)) + + tags.sort(key=lambda x: x[0]) + latest = tags[-1] + return latest + + +def update_actions() -> None: + root = Path(__file__).parent / "templates" + templates = root.rglob("*.yaml") + + action_re = re.compile(r"uses: ([\w\-/]+)@([\w\-.]+)(\W*#.*)?") + + for template in templates: + print("Updating", template) + + with template.open("r") as f: + content = f.read() + + def replace_action(match: re.Match[str]) -> str: + latest = latest_action_version(match.group(1)) + + print( + f" Updating {match.group(1)} from {match.group(2)} to {latest[2]} ({latest[1]})" + ) + return f"uses: {match.group(1)}@{latest[2]} # {latest[1]}" + + new_content = action_re.sub(replace_action, content) + + with template.open("w") as f: + f.write(new_content) + + +def main(): + parser = argparse.ArgumentParser() + subcommand = parser.add_subparsers(dest="subcommand", required=True) + + generate = subcommand.add_parser("generate") + generate.add_argument("repository", type=Path) + + subcommand.add_parser("update-actions") + + args = parser.parse_args() + + if args.subcommand == "generate": + return generate_workflows(args) + elif args.subcommand == "update-actions": + update_actions() + return 0 + else: + parser.print_help() + return 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pixi.lock b/pixi.lock index fc302df..9bc232d 100644 --- a/pixi.lock +++ b/pixi.lock @@ -35,6 +35,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/83/a2960d2c975836daa629a73995134fd86520c101412578c57da3d2aa71ee/doit-0.36.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl @@ -70,6 +72,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/83/a2960d2c975836daa629a73995134fd86520c101412578c57da3d2aa71ee/doit-0.36.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl @@ -125,6 +129,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/83/a2960d2c975836daa629a73995134fd86520c101412578c57da3d2aa71ee/doit-0.36.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5d/da/29a3c808bfb42ba86e5aca226fad7871b65fc216e18e14190553a879157b/pygit2-1.18.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl @@ -168,6 +174,8 @@ environments: - pypi: https://files.pythonhosted.org/packages/44/83/a2960d2c975836daa629a73995134fd86520c101412578c57da3d2aa71ee/doit-0.36.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/0e/ff/34dc8ce51f2f9ba39a5f2b34b9a5d70563cc93a387accf562c5c36e40d2b/pygit2-1.18.2-cp313-cp313-macosx_10_13_universal2.whl @@ -203,9 +211,10 @@ packages: - pypi: ./ name: adbc-drivers-dev version: '0.1' - sha256: 46967d9c79c2e61601109bc8152afde652c9ccc955927c122b0c8aac1340419a + sha256: 0045be89a16926cde2ceb4c8cfd5d40c4f84af0f5ae53278da08b3fd2af0233e requires_dist: - doit + - jinja2 - packaging - platformdirs - pygit2 @@ -366,6 +375,14 @@ packages: - pkg:pypi/iniconfig?source=hash-mapping size: 11474 timestamp: 1733223232820 +- pypi: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + name: jinja2 + version: 3.1.6 + sha256: 85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + requires_dist: + - markupsafe>=2.0 + - babel>=2.7 ; extra == 'i18n' + requires_python: '>=3.7' - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda sha256: 1a620f27d79217c1295049ba214c2f80372062fd251b569e9873d4a953d27554 md5: 0be7c6e070c19105f966d3758448d018 @@ -550,6 +567,16 @@ packages: purls: [] size: 46438 timestamp: 1727963202283 +- pypi: https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl + name: markupsafe + version: 3.0.3 + sha256: 116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 + requires_python: '>=3.9' +- pypi: https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: markupsafe + version: 3.0.3 + sha256: ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 + requires_python: '>=3.9' - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 md5: 47e340acb35de30501a76c7c799c41d7 diff --git a/pyproject.toml b/pyproject.toml index d4e3dfe..201ce7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ version = "0.1" dependencies = [ "doit", + "jinja2", "packaging", "platformdirs", "pygit2", @@ -30,6 +31,7 @@ dependencies = [ pre-commit-copyright = "adbc_drivers_dev.copyright:main" pre-commit-rat = "adbc_drivers_dev.rat:main" adbc-gen-package = "adbc_drivers_dev.package:main" +adbc-gen-workflow = "adbc_drivers_dev.workflow:main" adbc-make = "adbc_drivers_dev.make:main" adbc-release = "adbc_drivers_dev.release:main"