diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..3493fa0 --- /dev/null +++ b/action.yml @@ -0,0 +1,43 @@ +name: Post build status to Discord + +inputs: + webhook-url: + description: "Discord webhook URL events should be sent to." + required: true + title: + description: "Message title" + default: "${{github.repository}} (Build #${{github.run_number}})" + project-name: + description: "Project name." + required: true + status: + description: "Status to report." + required: true + variant: + description: "Style to attach to the message element." + default: "info" + init: + description: "Marks the first status push, prevents pulling context." + +runs: + using: "composite" + steps: + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: 3.12 + - uses: actions/download-artifact@v3 + if: ${{ !inputs.init }} + with: + name: "${{github.sha}}-${{github.run_number}}-${{github.run_attempt}}-discord-context.zip" + path: /tmp + - name: Install dependencies + if: ${{ always() }} + run: pip install -r ${{ github.action_path }}/requirements.txt + - run: python ${{ github.action_path }}/main.py ${{ inputs.webhook-url }} "${{inputs.title}}" ${{ inputs.status }} ${{ inputs.variant }} /tmp/discord-context + - uses: actions/upload-artifact@v3 + with: + path: /tmp/discord-context + name: "${{github.sha}}-${{github.run_number}}-${{github.run_attempt}}-discord-context.zip" + retention-days: 1 + overwrite: true diff --git a/main.py b/main.py new file mode 100644 index 0000000..603f1d6 --- /dev/null +++ b/main.py @@ -0,0 +1,76 @@ +import sys +import typing +import json +import pathlib +import datetime + +import httpx + +COLORS = { + "failure": 0xCB2431, + "success": 0x28A745, + "info": 0x0000FF, +} + + +class EmbedField(typing.TypedDict): + name: str + value: str + inline: bool + + +class Embed(typing.TypedDict): + color: str + title: str + fields: list[EmbedField] + + +class MessageState(typing.TypedDict): + """ + Partial mapping of Discord message information. + + See: https://discord.com/developers/docs/resources/channel#message-object + """ + + id: int + timestamp: str + edited_timestamp: str + embeds: list[Embed] + + +class Context(typing.TypedDict): + message: MessageState + + +if __name__ == "__main__": + webhook_url, title, status, variant, context_path = sys.argv[1:6] + context = None + if pathlib.Path(context_path).exists(): + with open(context_path, "r", encoding="utf-8") as context_file: + context = Context(**json.load(context_file)) + + embed_data = Embed( + color=COLORS[variant], + title=title, + fields=[ + EmbedField(name="Status", value=status, inline=True), + EmbedField( + name="Updated at", + value=str(datetime.datetime.now().ctime()), + inline=True, + ), + ], + ) + + if context: + message_id = context["message"]["id"] + response = httpx.patch( + f"{webhook_url}/messages/{message_id}?wait=true", + json={"embeds": [*context["message"]["embeds"], embed_data]}, + ) + else: + response = httpx.post(f"{webhook_url}?wait=true", json={"embeds": [embed_data]}) + context = Context(message=response.json()) + + with open(context_path, "w", encoding="utf-8") as context_file: + json.dump(context, context_file) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..610f441 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +httpx==0.27