-
Notifications
You must be signed in to change notification settings - Fork 0
172 lines (154 loc) · 7.34 KB
/
inbound-bridge.yml
File metadata and controls
172 lines (154 loc) · 7.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# Digital Service Orchestra — Inbound Bridge
# Pulls Jira changes into local ticket system on a scheduled interval.
name: Inbound Bridge
on:
schedule:
# Pull from Jira every 30 minutes. To change the interval, edit this cron
# expression directly — GitHub Actions does not support expressions in cron
# schedule values, so it cannot be set via a repository variable.
- cron: '*/30 * * * *'
workflow_dispatch: {}
concurrency:
group: jira-bridge
cancel-in-progress: false
jobs:
bridge:
name: Pull tickets from Jira
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
# Skip commits made by the bridge bot itself (echo prevention).
# Fall back to 'dso-bridge[bot]' when BRIDGE_BOT_LOGIN is unset so the
# guard is never silently bypassed (unset var would evaluate to '' and
# always pass, causing an infinite push loop on the tickets branch).
if: github.actor != (vars.BRIDGE_BOT_LOGIN || 'dso-bridge[bot]')
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
ref: tickets
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Cache ACLI jar
id: cache-acli
uses: actions/cache@v4
with:
path: ~/.acli
key: acli-${{ runner.os }}-${{ vars.ACLI_VERSION || 'latest' }}
- name: Validate ACLI version before download
if: steps.cache-acli.outputs.cache-hit != 'true'
run: |
# Reject unset or unpinned version strings BEFORE any download occurs so
# no untrusted artifact is fetched with an interpolated/attacker-controlled
# version path.
ACLI_VERSION="${{ vars.ACLI_VERSION }}"
if [ -z "$ACLI_VERSION" ] || [ "$ACLI_VERSION" = "latest" ]; then
echo "ERROR: ACLI_VERSION is unset or 'latest' — cannot proceed with download."
echo " Pin ACLI_VERSION to a specific release and set ACLI_SHA256 to its"
echo " expected SHA256 digest before running this workflow."
exit 1
fi
- name: Download ACLI
if: steps.cache-acli.outputs.cache-hit != 'true'
run: |
mkdir -p ~/.acli
curl -sSL "https://bobswift.atlassian.net/wiki/download/attachments/16285777/acli-${{ vars.ACLI_VERSION }}-distribution.zip" \
-o ~/.acli/acli.zip \
|| curl -sSL "https://packages.atlassian.com/maven/public/com/atlassian/cli/acli/${{ vars.ACLI_VERSION }}/acli-${{ vars.ACLI_VERSION }}.jar" \
-o ~/.acli/acli.jar
- name: Verify ACLI checksum
if: steps.cache-acli.outputs.cache-hit != 'true'
run: |
# Verify the downloaded artifact BEFORE extraction so the zip is still
# present on disk. Without a checksum, a compromised or MITM'd download
# could execute arbitrary code.
ACLI_VERSION="${{ vars.ACLI_VERSION }}"
ACLI_SHA256="${{ vars.ACLI_SHA256 }}"
if [ -z "$ACLI_VERSION" ] || [ "$ACLI_VERSION" = "latest" ]; then
echo "ERROR: ACLI_VERSION is unset or 'latest' — checksum verification cannot proceed."
echo " Pin ACLI_VERSION to a specific release and set ACLI_SHA256 to its expected"
echo " SHA256 digest before running this workflow. Unpinned downloads are rejected"
echo " to enforce supply-chain integrity."
exit 1
elif [ -z "$ACLI_SHA256" ]; then
echo "ERROR: ACLI_VERSION is pinned to '${ACLI_VERSION}' but ACLI_SHA256 is not set."
echo " Set the ACLI_SHA256 repository variable to the expected SHA256 of the downloaded artifact."
exit 1
else
# Verify whichever artifact was downloaded (zip takes priority).
if [ -f ~/.acli/acli.zip ]; then
ARTIFACT=~/.acli/acli.zip
else
ARTIFACT=~/.acli/acli.jar
fi
echo "${ACLI_SHA256} ${ARTIFACT}" | sha256sum -c --strict
echo "Checksum verified: ${ARTIFACT}"
fi
- name: Extract ACLI zip
if: steps.cache-acli.outputs.cache-hit != 'true'
run: |
# The download step attempts to fetch a zip first and falls back to a
# direct jar. If a zip was downloaded, extract the jar from it so the
# wrapper script can always reference ~/.acli/acli.jar consistently.
if [ -f ~/.acli/acli.zip ]; then
unzip -jo ~/.acli/acli.zip "*.jar" -d ~/.acli/
# Rename the extracted jar to the canonical name expected by the wrapper.
extracted=$(ls ~/.acli/*.jar 2>/dev/null | head -1)
if [ -z "$extracted" ]; then
echo "ERROR: No .jar found inside acli.zip" >&2
exit 1
fi
mv "$extracted" ~/.acli/acli.jar
rm -f ~/.acli/acli.zip
echo "Extracted acli.jar from zip."
else
echo "Direct jar download detected — no extraction needed."
fi
- name: Add ACLI to PATH
run: |
if [ ! -f "$HOME/.acli/acli.jar" ]; then
echo "ERROR: $HOME/.acli/acli.jar not found — download or extraction must have failed." >&2
exit 1
fi
printf '#!/usr/bin/env bash\nexec java -jar "$HOME/.acli/acli.jar" "$@"\n' > /usr/local/bin/acli
chmod +x /usr/local/bin/acli
- name: Run inbound bridge
id: run-bridge
run: |
python3 plugins/dso/scripts/bridge-inbound.py
env:
JIRA_URL: ${{ secrets.JIRA_URL }}
JIRA_USER: ${{ secrets.JIRA_USER }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
BRIDGE_ENV_ID: ${{ vars.BRIDGE_ENV_ID }}
GH_RUN_ID: ${{ github.run_id }}
INBOUND_CHECKPOINT_PATH: ${{ vars.INBOUND_CHECKPOINT_PATH || '.tickets-tracker/.inbound-checkpoint.json' }}
INBOUND_OVERLAP_BUFFER_MINUTES: ${{ vars.INBOUND_OVERLAP_BUFFER_MINUTES || '15' }}
INBOUND_STATUS_MAPPING: ${{ vars.INBOUND_STATUS_MAPPING || '{}' }}
INBOUND_TYPE_MAPPING: ${{ vars.INBOUND_TYPE_MAPPING || '{}' }}
- name: Commit CREATE events back to tickets branch
run: |
# Check if any new CREATE (or BRIDGE_ALERT) events were written
if git diff --quiet HEAD -- .tickets-tracker/; then
echo "No CREATE events to commit — skipping."
exit 0
fi
# Configure bridge bot identity (not the default github-actions[bot]).
# Values are read from env vars (set via the env: map below) rather than
# interpolated directly into the run: block to prevent expression injection.
git config user.name "${BRIDGE_BOT_NAME}"
git config user.email "${BRIDGE_BOT_EMAIL}"
git add .tickets-tracker/
git commit -m "chore: sync CREATE events from Jira inbound bridge [run ${{ github.run_id }}]"
git push origin HEAD:tickets
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRIDGE_BOT_NAME: ${{ vars.BRIDGE_BOT_NAME || 'dso-bridge[bot]' }}
BRIDGE_BOT_EMAIL: ${{ vars.BRIDGE_BOT_EMAIL || 'dso-bridge@users.noreply.github.com' }}
- name: Job timing report
if: always()
run: echo "inbound-bridge completed at $(date -u +%Y-%m-%dT%H:%M:%SZ)"