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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions .github/workflows/catalog-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,32 @@ jobs:
steps:
- name: Validate GitHub App configuration
env:
WORKFLOW_APP_ID: ${{ vars.LIBRECODE_WORKFLOW_APP_ID }}
WORKFLOW_APP_PRIVATE_KEY: ${{ secrets.LIBRECODE_WORKFLOW_APP_PRIVATE_KEY }}
run: |
if [ -z "$WORKFLOW_APP_ID" ]; then
echo "::error::LIBRECODE_WORKFLOW_APP_ID is not configured."
exit 1
fi
if [ -z "$WORKFLOW_APP_PRIVATE_KEY" ]; then
echo "::error::LIBRECODE_WORKFLOW_APP_PRIVATE_KEY is not configured."
exit 1
fi

- name: Resolve GitHub App client id
id: app-identity
env:
GH_TOKEN: ${{ github.token }}
WORKFLOW_APP_SLUG: librecode-workflow-automation
run: |
set -euo pipefail
client_id="$(gh api "/apps/${WORKFLOW_APP_SLUG}" --jq .client_id)"
if [ -z "$client_id" ]; then
echo "::error::GitHub App metadata did not contain a client_id."
exit 1
fi
echo "client-id=$client_id" >> "$GITHUB_OUTPUT"

- name: Create GitHub App token
id: app-token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ vars.LIBRECODE_WORKFLOW_APP_ID }}
client-id: ${{ steps.app-identity.outputs.client-id }}
private-key: ${{ secrets.LIBRECODE_WORKFLOW_APP_PRIVATE_KEY }}
owner: LibreCodeCoop
repositories: .github
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.6.23
0.6.24
49 changes: 34 additions & 15 deletions actions/release-post-merge/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ inputs:
github-token:
description: Caller token used for artifact restore and permission lookup.
required: true
app-id:
description: GitHub App id used for short-lived mutation tokens.
required: true
app-slug:
description: Public GitHub App slug used to resolve its client id.
required: false
default: librecode-workflow-automation
app-private-key:
description: GitHub App private key used for short-lived mutation tokens.
required: true
Expand Down Expand Up @@ -52,17 +53,13 @@ runs:
- name: Validate GitHub App credentials
shell: bash
env:
RELEASE_APP_ID: ${{ inputs.app-id }}
RELEASE_APP_SLUG: ${{ inputs.app-slug }}
RELEASE_APP_PRIVATE_KEY: ${{ inputs.app-private-key }}
run: |
set -euo pipefail

if [[ -z "${RELEASE_APP_ID}" ]]; then
echo "::error::GitHub App id is empty. Configure LIBRECODE_WORKFLOW_APP_ID as an Actions variable in the consumer repository or organization."
exit 1
fi
if [[ ! "${RELEASE_APP_ID}" =~ ^[0-9]+$ ]]; then
echo "::error::GitHub App id must be numeric."
if [[ -z "${RELEASE_APP_SLUG}" ]]; then
echo "::error::GitHub App slug is empty."
exit 1
fi
if [[ -z "${RELEASE_APP_PRIVATE_KEY}" ]]; then
Expand Down Expand Up @@ -116,11 +113,33 @@ runs:
echo "owner=${RELEASE_REPOSITORY%%/*}" >> "${GITHUB_OUTPUT}"
echo "name=${RELEASE_REPOSITORY#*/}" >> "${GITHUB_OUTPUT}"

- id: app-identity
name: Resolve GitHub App client id
shell: bash
env:
RELEASE_APP_SLUG: ${{ inputs.app-slug }}
RELEASE_GITHUB_TOKEN: ${{ inputs.github-token }}
RELEASE_GITHUB_API_URL: ${{ github.api_url }}
run: |
set -euo pipefail

app_json="$(curl --fail --silent --show-error --location \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${RELEASE_GITHUB_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${RELEASE_GITHUB_API_URL}/apps/${RELEASE_APP_SLUG}")"
client_id="$(php -r '$d=json_decode(stream_get_contents(STDIN),true,512,JSON_THROW_ON_ERROR); echo $d["client_id"] ?? "";' <<< "${app_json}")"
if [[ -z "${client_id}" ]]; then
echo "::error::GitHub App metadata did not contain a client_id for ${RELEASE_APP_SLUG}."
exit 1
fi
echo "client-id=${client_id}" >> "${GITHUB_OUTPUT}"

- id: finalization-token
name: Create finalization token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ inputs.app-id }}
client-id: ${{ steps.app-identity.outputs.client-id }}
private-key: ${{ inputs.app-private-key }}
owner: ${{ steps.repository.outputs.owner }}
repositories: ${{ steps.repository.outputs.name }}
Expand Down Expand Up @@ -180,9 +199,9 @@ runs:

- id: draft-token
name: Create release draft token
uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ inputs.app-id }}
client-id: ${{ steps.app-identity.outputs.client-id }}
private-key: ${{ inputs.app-private-key }}
owner: ${{ steps.repository.outputs.owner }}
repositories: ${{ steps.repository.outputs.name }}
Expand Down Expand Up @@ -234,7 +253,7 @@ runs:
} >> "${GITHUB_STEP_SUMMARY}"

- name: Persist finalized release contracts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ steps.draft.outputs.state-artifact-name }}
path: ${{ runner.temp }}/release-post-merge-state
Expand Down
10 changes: 7 additions & 3 deletions tests/test_release_post_merge_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ def test_credentials_are_validated_before_artifact_restore(self) -> None:
validate = content.index("Validate GitHub App credentials")
restore = content.index("Restore preparation contracts")
self.assertLess(validate, restore)
self.assertIn("LIBRECODE_WORKFLOW_APP_ID", content)
self.assertIn("LIBRECODE_WORKFLOW_APP_PRIVATE_KEY", content)
self.assertIn("librecode-workflow-automation", content)
self.assertIn("/apps/${RELEASE_APP_SLUG}", content)
self.assertNotIn("\n app-id:", content)
self.assertNotIn("inputs.app-id", content)

def test_authorization_happens_before_mutation(self) -> None:
content = ACTION.read_text(encoding="utf-8")
Expand All @@ -31,7 +34,8 @@ def test_authorization_happens_before_mutation(self) -> None:

def test_mutating_stages_use_scoped_tokens(self) -> None:
content = ACTION.read_text(encoding="utf-8")
self.assertEqual(2, content.count("actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42"))
self.assertEqual(2, content.count("actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1"))
self.assertEqual(2, content.count("client-id: ${{ steps.app-identity.outputs.client-id }}"))
self.assertIn("permission-contents: write", content)
self.assertIn("permission-pull-requests: write", content)
self.assertNotIn("permission-issues: write", content)
Expand All @@ -51,7 +55,7 @@ def test_contract_chain_is_persisted_for_publication(self) -> None:
self.assertIn("milestone:transition", content)
self.assertIn("release:draft", content)
self.assertIn('artifact_name="release-state-${release_id}"', content)
self.assertIn("actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02", content)
self.assertIn("actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", content)

def test_restore_action_can_bind_artifact_to_origin_workflow(self) -> None:
content = RESTORE.read_text(encoding="utf-8")
Expand Down
Loading