-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
compatible: Add release_charm.yaml (#20)
## Issue [canonical/charming-actions/upload-charm](https://github.com/canonical/charming-actions/tree/main/upload-charm) does not support multiple series [DPE-1273](https://warthogs.atlassian.net/browse/DPE-1273) Also resolves [design bug](https://chat.canonical.com/canonical/pl/sjwya3bo3fd75ni4zbk5caqxsw) ## Solution Replace charming-actions/upload-charm with a reusable workflow + Python script (so it's easier to maintain)
- Loading branch information
1 parent
c0da434
commit dfee76d
Showing
7 changed files
with
311 additions
and
5 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
Workflow file: [build_charm_without_cache.yaml](build_charm_without_cache.yaml) | ||
|
||
## Usage | ||
```yaml | ||
# Copyright 2023 Canonical Ltd. | ||
# See LICENSE file for licensing details. | ||
jobs: | ||
build: | ||
name: Build charm | ||
uses: canonical/data-platform-workflows/.github/workflows/build_charm_without_cache.yaml@v2 | ||
``` |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Copyright 2023 Canonical Ltd. | ||
# See LICENSE file for licensing details. | ||
|
||
# Usage documentation: build_charm_without_cache.md | ||
|
||
on: | ||
workflow_call: | ||
inputs: | ||
artifact-name: | ||
description: Packed charm is uploaded to this GitHub artifact name | ||
default: charm-packed-without-cache | ||
type: string | ||
path-to-charm-directory: | ||
description: Relative path to charm directory from repository directory | ||
default: . | ||
type: string | ||
outputs: | ||
artifact-name: | ||
description: Packed charm is uploaded to this GitHub artifact name | ||
value: ${{ inputs.artifact-name }} | ||
|
||
jobs: | ||
build: | ||
name: Build charm | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 60 | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v3 | ||
- name: Setup environment | ||
# TODO: Replace with custom image on self-hosted runner | ||
run: | | ||
# Copied from https://github.com/charmed-kubernetes/actions-operator/blob/96fb0b07eb675f74cf1796be812bc7e67a0d62fc/src/bootstrap/index.ts#L151 | ||
sudo adduser $USER lxd | ||
newgrp lxd | ||
sudo lxd waitready | ||
sudo lxd init --auto | ||
sudo iptables -F FORWARD | ||
sudo iptables -P FORWARD ACCEPT | ||
sudo chmod a+wr /var/snap/lxd/common/lxd/unix.socket | ||
sudo snap install charmcraft --classic | ||
- name: Pack charm | ||
id: pack | ||
working-directory: ${{ inputs.path-to-charm-directory }} | ||
run: charmcraft pack | ||
- name: Upload charmcraft logs | ||
if: ${{ failure() && steps.pack.outcome == 'failure' }} | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: ${{ inputs.artifact-name }}-charmcraft-build-logs | ||
path: ~/.local/state/charmcraft/log/ | ||
if-no-files-found: error | ||
- name: Upload packed charm | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: ${{ inputs.artifact-name }} | ||
path: | | ||
${{ inputs.path-to-charm-directory }}/*.charm | ||
if-no-files-found: error |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
Workflow file: [release_charm.yaml](release_charm.yaml) | ||
|
||
## Usage | ||
Add `.yaml` file to `.github/workflows/` | ||
```yaml | ||
# Copyright 2023 Canonical Ltd. | ||
# See LICENSE file for licensing details. | ||
name: Release to Charmhub | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
build: | ||
name: Build charm | ||
uses: canonical/data-platform-workflows/.github/workflows/build_charm_without_cache.yaml@v2 | ||
|
||
release: | ||
name: Release charm | ||
needs: | ||
- build | ||
uses: canonical/data-platform-workflows/.github/workflows/release_charm.yaml@v2 | ||
with: | ||
channel: latest/edge | ||
artifact-name: ${{ needs.build.outputs.artifact-name }} | ||
secrets: | ||
charmhub-token: ${{ secrets.CHARMHUB_TOKEN }} | ||
permissions: | ||
contents: write # Needed to create GitHub release | ||
``` |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# Copyright 2023 Canonical Ltd. | ||
# See LICENSE file for licensing details. | ||
|
||
# Usage documentation: release_charm.md | ||
|
||
on: | ||
workflow_call: | ||
inputs: | ||
channel: | ||
description: Charmhub channel to release to | ||
required: true | ||
type: string | ||
artifact-name: | ||
description: Name of GitHub artifact that contains the .charm file(s) (use another workflow to build the charm) | ||
required: true | ||
type: string | ||
path-to-charm-directory: | ||
description: Relative path to charm directory (from GitHub artifact root) | ||
default: . | ||
type: string | ||
create-github-release: | ||
description: Create git tag & GitHub release | ||
default: true | ||
type: boolean | ||
secrets: | ||
charmhub-token: | ||
description: Charmhub login token | ||
required: true | ||
|
||
jobs: | ||
get-workflow-version: | ||
name: Get workflow version | ||
uses: ./.github/workflows/_get_workflow_version.yaml | ||
with: | ||
repository-name: canonical/data-platform-workflows | ||
file-name: release_charm.yaml | ||
|
||
release-charm: | ||
name: Release charm | ||
needs: | ||
- get-workflow-version | ||
runs-on: ubuntu-latest | ||
timeout-minutes: 30 | ||
steps: | ||
- name: Install pyyaml | ||
run: python3 -m pip install pyyaml | ||
- name: Install docker | ||
run: | | ||
sudo adduser $USER docker | ||
newgrp docker | ||
sudo snap install docker | ||
- name: Install charmcraft | ||
run: sudo snap install charmcraft --classic | ||
- name: Checkout caller repository | ||
uses: actions/checkout@v3 | ||
with: | ||
path: caller-repo | ||
- name: Checkout release workflow repository | ||
uses: actions/checkout@v3 | ||
with: | ||
repository: canonical/data-platform-workflows | ||
path: workflow-repo | ||
ref: ${{ needs.get-workflow-version.outputs.version }} | ||
- name: Download .charm file(s) | ||
uses: actions/download-artifact@v3 | ||
with: | ||
name: ${{ inputs.artifact-name }} | ||
path: caller-repo | ||
- name: Upload & release charm | ||
id: release | ||
working-directory: caller-repo | ||
run: python3 ../workflow-repo/release_charm.py --charm-directory ${{ inputs.path-to-charm-directory }} --channel ${{ inputs.channel }} | ||
env: | ||
CHARMCRAFT_AUTH: ${{ secrets.charmhub-token }} | ||
- name: Create GitHub release | ||
if: ${{ inputs.create-github-release }} | ||
working-directory: caller-repo | ||
run: | | ||
git tag ${{ steps.release.outputs.release_tag }} | ||
git push origin ${{ steps.release.outputs.release_tag }} | ||
gh release create ${{ steps.release.outputs.release_tag }} --verify-tag --generate-notes --title "${{ steps.release.outputs.release_title }}" --notes-file release_notes.txt | ||
env: | ||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
- name: Upload charmcraft logs | ||
if: ${{ failure() && steps.release.outcome == 'failure' }} | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: ${{ inputs.artifact-name }}-charmcraft-release-logs | ||
path: ~/.local/state/charmcraft/log/ | ||
if-no-files-found: error |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import argparse | ||
import dataclasses | ||
import json | ||
import logging | ||
import os | ||
import pathlib | ||
import subprocess | ||
import sys | ||
|
||
import yaml | ||
|
||
|
||
@dataclasses.dataclass | ||
class OCIResource: | ||
"""OCI image that has been uploaded to Charmhub as a charm resource""" | ||
|
||
resource_name: str | ||
revision: int | ||
|
||
|
||
def run(command_: list): | ||
"""Run subprocess command & log stderr | ||
Returns: | ||
stdout | ||
""" | ||
process = subprocess.run(command_, capture_output=True, encoding="utf-8") | ||
try: | ||
process.check_returncode() | ||
except subprocess.CalledProcessError as e: | ||
logging.error(e.stderr) | ||
raise | ||
return process.stdout | ||
|
||
|
||
logging.basicConfig(level=logging.INFO, stream=sys.stdout) | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("--charm-directory") | ||
parser.add_argument("--channel") | ||
args = parser.parse_args() | ||
charm_directory = pathlib.Path(args.charm_directory) | ||
|
||
# Upload charm file(s) & store revision | ||
charm_revisions: list[int] = [] | ||
for charm_file in charm_directory.glob("*.charm"): | ||
logging.info(f"Uploading {charm_file=}") | ||
output = run(["charmcraft", "upload", "--format", "json", charm_file]) | ||
revision: int = json.loads(output)["revision"] | ||
logging.info(f"Uploaded charm {revision=}") | ||
charm_revisions.append(revision) | ||
assert len(charm_revisions) > 0, "No .charm files found" | ||
|
||
metadata_file = yaml.safe_load((charm_directory / "metadata.yaml").read_text()) | ||
charm_name = metadata_file["name"] | ||
|
||
# (Only for Kubernetes charms) upload OCI image(s) & store revision | ||
oci_resources: list[OCIResource] = [] | ||
resources = metadata_file.get("resources", {}) | ||
for resource_name, resource in resources.items(): | ||
if resource["type"] != "oci-image": | ||
continue | ||
image_name = resource["upstream-source"] | ||
logging.info(f"Downloading OCI image: {image_name}") | ||
run(["docker", "pull", image_name]) | ||
image_id = run(["docker", "image", "inspect", image_name, "--format", "'{{.Id}}'"]) | ||
image_id = image_id.rstrip("\n").strip("'").lstrip("sha256:") | ||
assert "\n" not in image_id, f"Multiple local images found for {image_name}" | ||
logging.info(f"Uploading charm resource: {resource_name}") | ||
output = run( | ||
[ | ||
"charmcraft", | ||
"upload-resource", | ||
"--format", | ||
"json", | ||
charm_name, | ||
resource_name, | ||
"--image", | ||
image_id, | ||
] | ||
) | ||
revision: int = json.loads(output)["revision"] | ||
logging.info(f"Uploaded charm resource {revision=}") | ||
oci_resources.append(OCIResource(resource_name, revision)) | ||
|
||
# Release charm file(s) | ||
for charm_revision in charm_revisions: | ||
logging.info(f"Releasing {charm_revision=}") | ||
command = [ | ||
"charmcraft", | ||
"release", | ||
charm_name, | ||
"--revision", | ||
str(charm_revision), | ||
"--channel", | ||
args.channel, | ||
] | ||
for oci in oci_resources: | ||
command += ["--resource", f"{oci.resource_name}:{oci.revision}"] | ||
run(command) | ||
|
||
# Output GitHub release info | ||
release_tag = f"rev{max(charm_revisions)}" | ||
if len(charm_revisions) == 1: | ||
release_title = "Revision " | ||
else: | ||
release_title = "Revisions " | ||
release_title += ", ".join(str(revision) for revision in charm_revisions) | ||
release_notes = f"Released to {args.channel}\nOCI images:\n" + "\n".join( | ||
f"- {dataclasses.asdict(oci)}" for oci in oci_resources | ||
) | ||
with open("release_notes.txt", "w") as file: | ||
file.write(release_notes) | ||
with open(os.environ["GITHUB_OUTPUT"], "a") as file: | ||
file.write(f"release_tag={release_tag}\nrelease_title={release_title}") |