-
Notifications
You must be signed in to change notification settings - Fork 0
179 lines (159 loc) · 7.29 KB
/
inbound-bridge.yml
File metadata and controls
179 lines (159 loc) · 7.29 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
173
174
175
176
177
178
179
# Digital Service Orchestra — Inbound Bridge
# Pulls Jira changes into local ticket system on a scheduled interval.
name: Inbound Bridge
on:
schedule:
- 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 main (scripts and plugin code)
uses: actions/checkout@v4
with:
fetch-depth: 1
ref: main
- name: Mount tickets branch as worktree
run: |
git fetch origin tickets --depth=1
# Remove the placeholder .tickets-tracker/ from main (if it exists)
rm -rf .tickets-tracker
git worktree add .tickets-tracker tickets
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Cache ACLI binary
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
# ACLI v1.3+ is a Go binary distributed as tar.gz from acli.atlassian.com.
# The runner is linux/amd64 (ubuntu-latest).
curl -sSL "https://acli.atlassian.com/linux/${{ vars.ACLI_VERSION }}/acli_${{ vars.ACLI_VERSION }}_linux_amd64.tar.gz" \
-o ~/.acli/acli.tar.gz
- name: Verify ACLI checksum
if: steps.cache-acli.outputs.cache-hit != 'true'
run: |
# Verify the downloaded artifact BEFORE extraction so a compromised
# or MITM'd download cannot execute arbitrary code.
ACLI_VERSION="${{ vars.ACLI_VERSION }}"
ACLI_SHA256="${{ vars.ACLI_SHA256 }}"
ARTIFACT=~/.acli/acli.tar.gz
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."
exit 1
elif [ -z "$ACLI_SHA256" ]; then
# Compute and log the hash so the operator can capture it from CI output
COMPUTED_HASH=$(sha256sum "$ARTIFACT" | awk '{print $1}')
echo "WARNING: ACLI_SHA256 is not set. Computed SHA256 of downloaded artifact:"
echo ""
echo " ACLI_SHA256=${COMPUTED_HASH}"
echo ""
echo "Set this value as a GitHub repository variable:"
echo " gh variable set ACLI_SHA256 --body '${COMPUTED_HASH}'"
echo ""
echo "Proceeding WITHOUT checksum verification (first-run bootstrap)."
else
echo "${ACLI_SHA256} ${ARTIFACT}" | sha256sum -c --strict
echo "Checksum verified: ${ARTIFACT}"
fi
- name: Extract ACLI binary
if: steps.cache-acli.outputs.cache-hit != 'true'
run: |
# ACLI v1.3+ is a Go binary in a tar.gz with a version-prefixed directory.
# --strip-components=1 removes the top-level dir (e.g., acli_1.3.14-stable_linux_amd64/).
tar xzf ~/.acli/acli.tar.gz -C ~/.acli/ --strip-components=1
chmod +x ~/.acli/acli
rm -f ~/.acli/acli.tar.gz
echo "Extracted acli binary from tar.gz."
- name: Add ACLI to PATH
run: |
if [ ! -f "$HOME/.acli/acli" ]; then
echo "ERROR: $HOME/.acli/acli not found — download or extraction must have failed." >&2
exit 1
fi
ln -sf "$HOME/.acli/acli" /usr/local/bin/acli
- name: Authenticate ACLI
run: |
# ACLI Go binary requires explicit auth login (no env var auto-detection).
# Pipe the API token to stdin via --token flag.
echo "$JIRA_API_TOKEN" | acli jira auth login \
--site "$JIRA_URL" \
--email "$JIRA_USER" \
--token
env:
JIRA_URL: ${{ vars.JIRA_URL }}
JIRA_USER: ${{ vars.JIRA_USER }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
- name: Run inbound bridge
id: run-bridge
run: |
python3 plugins/dso/scripts/bridge-inbound.py
env:
JIRA_URL: ${{ vars.JIRA_URL }}
JIRA_USER: ${{ vars.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 || '{}' }}
JIRA_PROJECT: ${{ vars.JIRA_PROJECT }}
- name: Commit CREATE events back to tickets branch
run: |
cd .tickets-tracker
# Check for any changes (staged, unstaged, or untracked)
if [ -z "$(git status --porcelain)" ]; then
echo "No new 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 -A
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)"