build / build (push) Successful in 10m53s
twine's --skip-existing is not a client-side flag: it asks the repository whether it supports the feature, and Gitea's PyPI registry does not advertise it, so the upload fails with UnsupportedConfiguration before it starts. Check the simple index for the version instead -- the same check-then-publish shape the npm step already uses.
236 lines
10 KiB
YAML
236 lines
10 KiB
YAML
name: release
|
|
|
|
on:
|
|
push:
|
|
tags: ["v*.*.*"]
|
|
# A release that dies halfway -- the runner OOMs, the host reboots, a job is
|
|
# missing a tool -- leaves the tag pushed and only part of the release
|
|
# published, and re-pushing a tag to retry it is both awkward and
|
|
# destructive. Dispatch republishes the SDKs for a tag that already has its
|
|
# plugin binaries, which is the half that fails: the plugin job is a 45
|
|
# minute build that either produced its artifacts or did not.
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: Tag to publish the SDKs for, e.g. v0.2.0
|
|
required: true
|
|
|
|
env:
|
|
GO_VERSION: "1.25.x"
|
|
NODE_VERSION: "20.x"
|
|
PYTHON_VERSION: "3.11"
|
|
DOTNET_VERSION: "8.0.x"
|
|
UPSTREAM_REPO: usr_unknown/terraform-provider-dokploy
|
|
UPSTREAM_DIR: dokploy-teraform
|
|
# Cloning and the release itself authenticate with the token Gitea injects
|
|
# into every run. Publishing packages does not -- see PACKAGES_TOKEN below.
|
|
GITEA_HOST: gitea.coolify.vojtkov.dev
|
|
GITEA_OWNER: usr_unknown
|
|
|
|
# Publishing to the package registries needs its own token. The one Actions
|
|
# injects authenticates fine -- it reads the registry and the API as the repo
|
|
# owner -- but every write comes back 401, under Bearer and Basic alike, and
|
|
# `permissions: packages: write` does not change that. So PACKAGES_TOKEN is a
|
|
# repository secret holding an access token with the `write:package` scope.
|
|
# Everything else here still uses the injected token.
|
|
|
|
jobs:
|
|
# The plugin binaries. Pulumi resolves them from this release via the
|
|
# PluginDownloadURL baked into the schema, so this has to land before anyone
|
|
# installs an SDK.
|
|
plugin:
|
|
# Only on a tag push. A dispatch is for republishing SDKs against a tag
|
|
# whose binaries are already uploaded, and goreleaser would collide with
|
|
# them. To rebuild the binaries themselves, delete the release and
|
|
# re-push the tag.
|
|
if: github.event_name == 'push'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
path: pulumi-dokploy
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
repository: ${{ env.UPSTREAM_REPO }}
|
|
path: ${{ env.UPSTREAM_DIR }}
|
|
token: ${{ secrets.GITEA_TOKEN }}
|
|
# checkout's default-branch lookup returns "not found" on this Gitea.
|
|
ref: main
|
|
|
|
- uses: actions/setup-go@v5
|
|
with:
|
|
go-version: ${{ env.GO_VERSION }}
|
|
cache: false
|
|
|
|
# A bridged Pulumi provider links the whole Terraform provider plus the
|
|
# Pulumi SDK into one ~100MB binary, and compiling that is memory-hungry.
|
|
# goreleaser defaults its parallelism to the CPU count, so several of
|
|
# those compiles overlap and the runner OOMs -- the v0.2.0 release died
|
|
# after 31 minutes with `compile: signal: killed` on darwin_amd64.
|
|
#
|
|
# Build one target at a time. It is slower in wall-clock but it is the
|
|
# difference between a release that finishes and one that does not.
|
|
# GOGC trades some CPU for a lower peak heap in the compiler itself.
|
|
- uses: goreleaser/goreleaser-action@v6
|
|
with:
|
|
version: latest
|
|
args: release --clean --parallelism 1
|
|
workdir: pulumi-dokploy
|
|
env:
|
|
# goreleaser creates the release and uploads binaries: repository
|
|
# write, which the injected token already has. Not a package write.
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
GOGC: "50"
|
|
|
|
sdks:
|
|
needs: plugin
|
|
# `always()` so a dispatch, where plugin is skipped rather than run, still
|
|
# gets here -- but not past a plugin job that actually failed.
|
|
if: always() && needs.plugin.result != 'failure' && needs.plugin.result != 'cancelled'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
path: pulumi-dokploy
|
|
# The workflow file comes from the dispatched branch; the tree to
|
|
# publish comes from the tag. On a push the two are the same thing.
|
|
ref: ${{ inputs.tag || github.ref }}
|
|
# Needed for the tag lookup below.
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
repository: ${{ env.UPSTREAM_REPO }}
|
|
path: ${{ env.UPSTREAM_DIR }}
|
|
token: ${{ secrets.GITEA_TOKEN }}
|
|
# checkout's default-branch lookup returns "not found" on this Gitea.
|
|
ref: main
|
|
|
|
- uses: actions/setup-go@v5
|
|
with:
|
|
go-version: ${{ env.GO_VERSION }}
|
|
cache: false
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
|
|
- uses: actions/setup-dotnet@v4
|
|
with:
|
|
dotnet-version: ${{ env.DOTNET_VERSION }}
|
|
|
|
# tfgen converts the upstream provider's documentation examples into
|
|
# each language by shelling out to `pulumi convert`, and asserts rather
|
|
# than degrades when the binary is absent: "pulumi executable not in
|
|
# PATH". It only started mattering once UpstreamRepoPath gave tfgen
|
|
# docs to find, which is why v0.1.0 published without this.
|
|
- uses: pulumi/actions@v6
|
|
|
|
# On a tag push the ref name is the tag; on a dispatch it is the branch,
|
|
# so ask git what tag this commit carries. --exact-match keeps a dispatch
|
|
# from quietly publishing an untagged commit under the previous version.
|
|
- name: Derive version from tag
|
|
working-directory: pulumi-dokploy
|
|
run: |
|
|
TAG="$(git describe --tags --exact-match)"
|
|
echo "VERSION=${TAG#v}" >> "$GITHUB_ENV"
|
|
|
|
- name: Build SDKs
|
|
working-directory: pulumi-dokploy
|
|
run: make build_sdks VERSION=${{ env.VERSION }}
|
|
|
|
- name: Publish to the Gitea npm registry
|
|
working-directory: pulumi-dokploy/sdk/nodejs/bin
|
|
env:
|
|
TOKEN: ${{ secrets.PACKAGES_TOKEN }}
|
|
run: |
|
|
cat > .npmrc <<EOF
|
|
@maxvojtkov:registry=https://${GITEA_HOST}/api/packages/${GITEA_OWNER}/npm/
|
|
//${GITEA_HOST}/api/packages/${GITEA_OWNER}/npm/:_authToken=${TOKEN}
|
|
EOF
|
|
# A re-run of a half-finished release must get past the registries
|
|
# that already have this version -- Gitea answers a repeat publish
|
|
# with 409 and npm treats that as fatal.
|
|
if npm view "@maxvojtkov/pulumi-dokploy@${VERSION}" version >/dev/null 2>&1; then
|
|
echo "@maxvojtkov/pulumi-dokploy@${VERSION} is already published"
|
|
else
|
|
npm publish
|
|
fi
|
|
|
|
- name: Publish to the Gitea PyPI registry
|
|
working-directory: pulumi-dokploy/sdk/python
|
|
env:
|
|
TWINE_USERNAME: ${{ env.GITEA_OWNER }}
|
|
TWINE_PASSWORD: ${{ secrets.PACKAGES_TOKEN }}
|
|
run: |
|
|
python -m pip install --upgrade build twine
|
|
python -m build
|
|
# twine's --skip-existing is refused outright here: it asks the
|
|
# repository whether it supports the feature and Gitea's PyPI
|
|
# registry does not advertise it ("UnsupportedConfiguration"). Ask
|
|
# the simple index instead, the same check-then-publish shape the
|
|
# npm step uses.
|
|
INDEX="https://${GITEA_HOST}/api/packages/${GITEA_OWNER}/pypi/simple/pulumi-dokploy/"
|
|
if curl -sf --user "${GITEA_OWNER}:${TWINE_PASSWORD}" "$INDEX" \
|
|
| grep -qE "pulumi_dokploy-${VERSION}[-.]"; then
|
|
echo "pulumi-dokploy ${VERSION} is already published"
|
|
else
|
|
python -m twine upload \
|
|
--repository-url "https://${GITEA_HOST}/api/packages/${GITEA_OWNER}/pypi" \
|
|
dist/*
|
|
fi
|
|
|
|
- name: Publish to the Gitea NuGet registry
|
|
working-directory: pulumi-dokploy/sdk/dotnet
|
|
env:
|
|
TOKEN: ${{ secrets.PACKAGES_TOKEN }}
|
|
run: |
|
|
# The generated csproj sets GeneratePackageOnBuild, and `dotnet pack`
|
|
# on such a project skips compiling -- it assumes the build already
|
|
# packed -- then fails with NU5026 because the assembly it wants to
|
|
# pack was never produced. Turn the property off for this invocation
|
|
# and pack builds the project itself, as it normally would.
|
|
dotnet pack --configuration Release --output ./nupkg \
|
|
-p:GeneratePackageOnBuild=false
|
|
dotnet nuget push ./nupkg/*.nupkg \
|
|
--source "https://${GITEA_HOST}/api/packages/${GITEA_OWNER}/nuget/index.json" \
|
|
--api-key "$TOKEN" --skip-duplicate
|
|
|
|
# The Go SDK's module path stays github.com/maxvojtkov/..., which no
|
|
# amount of Gitea hosting changes. Uploading it to Gitea's Go registry is
|
|
# what makes that path resolvable anyway: consumers point GOPROXY here.
|
|
#
|
|
# A module zip must have every entry prefixed `<module>@<version>/` and
|
|
# must contain no directory entries at all -- hence `zip -D`, without
|
|
# which `go get` fails with "has unexpected file".
|
|
- name: Publish the Go SDK to the Gitea Go registry
|
|
working-directory: pulumi-dokploy
|
|
env:
|
|
TOKEN: ${{ secrets.PACKAGES_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
MODULE=github.com/maxvojtkov/pulumi-dokploy/sdk
|
|
STAGE="$(mktemp -d)"
|
|
DEST="$STAGE/$MODULE@v${VERSION}"
|
|
mkdir -p "$DEST"
|
|
cp sdk/go.mod sdk/go.sum "$DEST/"
|
|
cp -R sdk/go "$DEST/"
|
|
(cd "$STAGE" && zip -qrD "$STAGE/sdk.zip" . -x 'sdk.zip')
|
|
# 409 means this version is already in the registry, which is the
|
|
# expected answer when a partly finished release is re-run.
|
|
CODE="$(curl -sS -o /dev/null -w '%{http_code}' -X PUT \
|
|
--user "${GITEA_OWNER}:${TOKEN}" \
|
|
--upload-file "$STAGE/sdk.zip" \
|
|
"https://${GITEA_HOST}/api/packages/${GITEA_OWNER}/go/upload")"
|
|
case "$CODE" in
|
|
201) echo "Published $MODULE@v${VERSION}" ;;
|
|
409) echo "$MODULE@v${VERSION} is already published" ;;
|
|
*) echo "Go registry upload failed with HTTP $CODE"; exit 1 ;;
|
|
esac
|