Skip to content

Commit f5dcae5

Browse files
Publish draft version (#14)
* add release id output to publish-github-release * publish draft version * make github_token input optional * use github.token in workflow * add step to trigger release workflow * trigger release workflow * add release workflow ref * remove temp workflows * pass NA for releas ID when draft is set to true: * fix get run id * update permissions in readme * revert readme teble format change * Update publish-github-release/action.yml Co-authored-by: GabinL21 <[email protected]> * increase sleep after publishing release * switch releaseTagName to version --------- Co-authored-by: GabinL21 <[email protected]>
1 parent 09c557f commit f5dcae5

File tree

2 files changed

+153
-19
lines changed

2 files changed

+153
-19
lines changed

publish-github-release/README.md

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,52 @@ directly from a Jira release version, or it can use release notes provided direc
55

66
This action uses the GitHub CLI to create the release and a Python script to interact with the Jira API.
77

8+
## Duplicate Release Handling
9+
10+
The action automatically checks for existing releases with the same title before creating a new one:
11+
12+
- **When `draft=true`**: If a release with the same title already exists, the action logs a warning and skips creation without failing.
13+
- **When `draft=false`**: If an existing draft release with the same title is found, it will be published instead of creating a new release. If a published release with the same title already exists, the action will fail with an error.
14+
15+
## Release Workflow Triggering
16+
17+
After creating the GitHub release, this action automatically triggers a release workflow in the caller repository using the GitHub CLI. The action:
18+
19+
- Triggers the specified release workflow (default: `release.yml`) on the specified branch or ref (default: `master`)
20+
- Passes the release tag name, release ID, and dry-run flag (based on the `draft` input) to the triggered workflow
21+
- Monitors the workflow execution and waits for it to complete
22+
- Succeeds if the release workflow completes successfully, or fails if the release workflow fails
23+
24+
This ensures that the entire release process (GitHub release creation + downstream release workflow) succeeds or fails as a unit.
25+
826
## Prerequisites
927

1028
To fetch release notes from Jira, the action requires that the repository has the `development/kv/data/jira` token
1129
configured in vault.
1230
This can be done using the SPEED self-service
1331
portal ([more info](https://xtranet-sonarsource.atlassian.net/wiki/spaces/Platform/pages/3553787989/Manage+Vault+Policy+-+SPEED)).
1432

15-
The action also requires a `github_token` with `contents: write` permissions to create the release. The default
16-
`${{ github.token }}` is usually sufficient.
33+
The action also requires a `github_token` with `contents: write`, `id-token:write` and `actions:write` permissions to create the release.
1734

1835
## Inputs
1936

2037
The following inputs can be configured for the action:
2138

22-
| Input | Description | Required | Default |
23-
|--------------------------|------------------------------------------------------------------------------------------------------------------|----------|-----------------------|
24-
| `github_token` | The GitHub token for API calls. | `true` | `${{ github.token }}` |
25-
| `version` | The version number for the new release (e.g., `v1.0.0`). This will also be the tag name. | `true` | |
26-
| `branch` | The branch, commit, or tag to create the release from. | `false` | `master` |
27-
| `draft` | A boolean value to indicate if the release should be a draft. | `false` | `true` |
28-
| `release_notes` | The full markdown content for the release notes. If provided, this is used directly, ignoring Jira inputs. | `false` | `''` |
29-
| `jira_release_name` | The name of the Jira release version. If provided and `release_notes` is empty, notes will be fetched from Jira. | `false` | `''` |
30-
| `jira_project_key` | The Jira project key (e.g., "SONARPHP") to fetch notes from. Required if using `jira_release_name`. | `false` | |
31-
| `jira_user` | Jira user (email) for authentication. Required if using `jira_release_name`. | `false` | |
32-
| `jira_token` | Jira API token for authentication. Required if using `jira_release_name`. | `false` | |
33-
| `issue_types` | Optional comma-separated list of Jira issue types to include in the release notes, in order of appearance. | `false` | `''` |
34-
| `use_sandbox` | Set to `false` to use the Jira production server instead of the sandbox. | `false` | `true` |
39+
| Input | Description | Required | Default |
40+
|------------------------|------------------------------------------------------------------------------------------------------------------|----------|-----------------------|
41+
| `github_token` | The GitHub token for API calls. | `true` | `${{ github.token }}` |
42+
| `version` | The version number for the new release (e.g., `v1.0.0`). This will also be the tag name. | `true` | |
43+
| `branch` | The branch, commit, or tag to create the release from. | `false` | `master` |
44+
| `draft` | A boolean value to indicate if the release should be a draft. | `false` | `true` |
45+
| `release_notes` | The full markdown content for the release notes. If provided, this is used directly, ignoring Jira inputs. | `false` | `''` |
46+
| `jira_release_name` | The name of the Jira release version. If provided and `release_notes` is empty, notes will be fetched from Jira. | `false` | `''` |
47+
| `jira_project_key` | The Jira project key (e.g., "SONARPHP") to fetch notes from. Required if using `jira_release_name`. | `false` | |
48+
| `jira_user` | Jira user (email) for authentication. Required if using `jira_release_name`. | `false` | |
49+
| `jira_token` | Jira API token for authentication. Required if using `jira_release_name`. | `false` | |
50+
| `issue_types` | Optional comma-separated list of Jira issue types to include in the release notes, in order of appearance. | `false` | `''` |
51+
| `use_sandbox` | Set to `false` to use the Jira production server instead of the sandbox. | `false` | `true` |
52+
| `release_workflow` | The filename of the release workflow to trigger in the caller repository. | `false` | `release.yml` |
53+
| `release_workflow_ref` | The branch or ref to trigger the release workflow from. | `false` | `master` |
3554

3655
## Outputs
3756

publish-github-release/action.yml

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,14 @@ inputs:
4343
description: 'The GitHub token for API calls.'
4444
required: true
4545
default: ${{ github.token }}
46-
wait_for_workflow_name:
47-
description: 'The name or file name of the workflow to wait for upon a non-draft release (e.g., "sonar-release" or "release.yml"). If empty, this step is skipped.'
46+
release_workflow:
47+
description: 'The filename of the release workflow to trigger in the caller repository.'
4848
required: false
49-
default: 'sonar-release'
49+
default: 'release.yml'
50+
release_workflow_ref:
51+
description: 'The branch or ref to trigger the release workflow from.'
52+
required: false
53+
default: 'master'
5054

5155
outputs:
5256
release_url:
@@ -117,15 +121,126 @@ runs:
117121
run: |
118122
echo "${{ inputs.github_token }}" | gh auth login --with-token
119123
124+
# Check if a release with the same title already exists
125+
EXPECTED_TITLE="${{ inputs.version }}"
126+
EXISTING_RELEASE=$(gh api repos/${{ github.repository }}/releases --jq ".[] | select(.name == \"$EXPECTED_TITLE\")" || echo "")
127+
128+
if [[ -n "$EXISTING_RELEASE" ]]; then
129+
EXISTING_DRAFT=$(echo "$EXISTING_RELEASE" | jq -r '.draft')
130+
EXISTING_TAG=$(echo "$EXISTING_RELEASE" | jq -r '.tag_name')
131+
EXISTING_URL=$(echo "$EXISTING_RELEASE" | jq -r '.html_url')
132+
EXISTING_ID=$(echo "$EXISTING_RELEASE" | jq -r '.id')
133+
134+
if [[ "${{ inputs.draft }}" == "true" ]]; then
135+
# If draft=true and release exists, log warning and do nothing
136+
echo "::warning::A release with title '$EXPECTED_TITLE' already exists. Skipping creation since draft=true."
137+
echo "release_url=${EXISTING_URL}" >> $GITHUB_OUTPUT
138+
echo "release_id=${EXISTING_ID}" >> $GITHUB_OUTPUT
139+
exit 0
140+
else
141+
if [[ "$EXISTING_DRAFT" == "true" ]]; then
142+
# If draft=false and existing release is a draft, publish it
143+
echo "Found existing draft release with title '$EXPECTED_TITLE'. Publishing it instead of creating a new one."
144+
gh release edit "$EXISTING_TAG" --draft=false
145+
echo "release_url=${EXISTING_URL}" >> $GITHUB_OUTPUT
146+
echo "release_id=${EXISTING_ID}" >> $GITHUB_OUTPUT
147+
exit 0
148+
else
149+
# If draft=false and existing release is already published, this is an error
150+
echo "::error::A published release with title '$EXPECTED_TITLE' already exists. Cannot create or publish another release with the same title."
151+
exit 1
152+
fi
153+
fi
154+
fi
155+
156+
# No existing release found, proceed with normal creation
120157
DRAFT_FLAG=""
121158
if [[ "${{ inputs.draft }}" == "true" ]]; then
122159
DRAFT_FLAG="--draft"
123160
fi
124161
125162
RELEASE_URL=$(gh release create "${{ inputs.version }}" \
126163
--target "${{ inputs.branch }}" \
127-
--title "${{ inputs.project_name }} ${{ inputs.version }}" \
164+
--title "${{ inputs.version }}" \
128165
--notes-file "release-notes.md" \
129166
$DRAFT_FLAG)
130167
131168
echo "release_url=${RELEASE_URL}" >> $GITHUB_OUTPUT
169+
170+
# Get the release ID only for published releases
171+
if [[ "${{ inputs.draft }}" != "true" ]]; then
172+
RELEASE_ID=$(gh api repos/${{ github.repository }}/releases/tags/${{ inputs.version }} --jq '.id')
173+
echo "release_id=${RELEASE_ID}" >> $GITHUB_OUTPUT
174+
fi
175+
176+
- name: Trigger Release Workflow
177+
shell: bash
178+
run: |
179+
echo "${{ inputs.github_token }}" | gh auth login --with-token
180+
181+
# Set release ID based on draft status
182+
if [[ "${{ inputs.draft }}" == "true" ]]; then
183+
RELEASE_ID_VALUE="N/A"
184+
else
185+
RELEASE_ID_VALUE="${{ steps.create_release.outputs.release_id }}"
186+
fi
187+
188+
echo "Triggering release workflow '${{ inputs.release_workflow }}' with tag '${{ inputs.version }}', release ID '$RELEASE_ID_VALUE', and dryRun=${{ inputs.draft }}..."
189+
190+
# Trigger the workflow
191+
gh workflow run "${{ inputs.release_workflow }}" \
192+
--repo "${{ github.repository }}" \
193+
--ref "${{ inputs.release_workflow_ref }}" \
194+
-f "version=${{ inputs.version }}" \
195+
-f "releaseId=$RELEASE_ID_VALUE" \
196+
-f "dryRun=${{ inputs.draft }}"
197+
198+
echo "Workflow triggered successfully"
199+
200+
# Wait a moment for the workflow to start, then get the run ID
201+
sleep 30
202+
203+
RUN_ID=$(gh run list \
204+
--repo "${{ github.repository }}" \
205+
--workflow "${{ inputs.release_workflow }}" \
206+
--limit 1 \
207+
--json databaseId \
208+
--jq '.[0].databaseId')
209+
210+
if [[ -z "$RUN_ID" ]] || [[ "$RUN_ID" == "null" ]]; then
211+
echo "::error::Failed to get workflow run ID"
212+
exit 1
213+
fi
214+
215+
echo "Monitoring workflow run ID: $RUN_ID"
216+
217+
# Wait for the workflow to complete
218+
while true; do
219+
RUN_STATUS=$(gh run view "$RUN_ID" \
220+
--repo "${{ github.repository }}" \
221+
--json status,conclusion \
222+
--jq '{status: .status, conclusion: .conclusion}')
223+
224+
STATUS=$(echo "$RUN_STATUS" | jq -r '.status')
225+
CONCLUSION=$(echo "$RUN_STATUS" | jq -r '.conclusion')
226+
227+
echo "Workflow status: $STATUS, conclusion: $CONCLUSION"
228+
229+
if [[ "$STATUS" == "completed" ]]; then
230+
if [[ "$CONCLUSION" == "success" ]]; then
231+
echo "✅ Release workflow completed successfully!"
232+
break
233+
else
234+
echo "::error::❌ Release workflow failed with conclusion: $CONCLUSION"
235+
gh run view "$RUN_ID" --repo "${{ github.repository }}" --log-failed
236+
exit 1
237+
fi
238+
elif [[ "$STATUS" == "cancelled" ]] || [[ "$STATUS" == "failure" ]]; then
239+
echo "::error::❌ Release workflow was cancelled or failed"
240+
gh run view "$RUN_ID" --repo "${{ github.repository }}" --log-failed
241+
exit 1
242+
fi
243+
244+
# Wait 15 seconds before checking again
245+
sleep 15
246+
done

0 commit comments

Comments
 (0)