Merge ~paelzer/ubuntu-helpers:os-import-for-focal into ~paelzer/ubuntu-helpers:master

Proposed by Christian Ehrhardt 
Status: Merged
Merge reported by: Christian Ehrhardt 
Merged at revision: c51ab9ea25f5f285d52fd2ee39551f8f7f4273b7
Proposed branch: ~paelzer/ubuntu-helpers:os-import-for-focal
Merge into: ~paelzer/ubuntu-helpers:master
Diff against target: 4962 lines (+4261/-36)
66 files modified
.gitignore (+8/-0)
README.md (+218/-0)
bryce/binit (+17/-0)
bryce/check-scripts (+100/-0)
bryce/cl-diff (+7/-0)
bryce/columns (+27/-0)
bryce/debb (+26/-0)
bryce/debian-changes (+14/-0)
bryce/dependency-tree (+59/-0)
bryce/enable-debsrc (+28/-0)
bryce/enable-proposed (+38/-0)
bryce/git-cmp-branches (+7/-0)
bryce/git-cmp-repos (+27/-0)
bryce/git-exam (+45/-0)
bryce/git-grep (+35/-0)
bryce/git-log-branch (+4/-0)
bryce/git-pop (+24/-0)
bryce/gu-set-target (+108/-0)
bryce/gu-whitelist (+119/-0)
bryce/install-build-deps (+11/-0)
bryce/json-check (+21/-0)
bryce/json-get (+43/-0)
bryce/ls-installed (+3/-0)
bryce/ls-lxc (+3/-0)
bryce/middle-paste (+14/-0)
bryce/migration-filter (+217/-0)
bryce/monday-triage (+14/-0)
bryce/mv-smart (+52/-0)
bryce/pkg-clean (+36/-0)
bryce/pkg-component (+37/-0)
bryce/pkg-depends (+39/-0)
bryce/pkg-nochange (+177/-0)
bryce/pkg-rdepends (+9/-0)
bryce/pkg-source (+27/-0)
bryce/prepend (+26/-0)
bryce/rmad (+16/-0)
bryce/shuffle (+11/-0)
cpaelzer/TrelloToJira.py (+75/-0)
cpaelzer/autopkgtests-running.sh (+6/-0)
cpaelzer/build-for-ppa.sh (+30/-0)
cpaelzer/buildpkg-qemu.sh (+4/-0)
cpaelzer/check-autopkgtest-stats.sh (+55/-15)
cpaelzer/dput (+10/-0)
cpaelzer/excuses-top-blocker.sh (+4/-0)
cpaelzer/get-packages-subscribed.py (+1/-1)
cpaelzer/git-to-dquilt.sh (+73/-17)
cpaelzer/lp-test-isrunning (+61/-0)
cpaelzer/lp-test-ppa (+395/-0)
cpaelzer/rma (+6/-0)
cpaelzer/update-git-ubuntu-urls.sh (+27/-0)
cpaelzer/versioned-rdepends.sh (+6/-0)
dev/null (+0/-3)
rbasak/adopt.py (+80/-0)
rbasak/chdist-if-migrated (+73/-0)
rbasak/standup_timer.py (+44/-0)
uscards/AUTHORS.md (+1/-0)
uscards/LICENSE.AGPL (+619/-0)
uscards/README (+1/-0)
uscards/README.md (+26/-0)
uscards/docs/design.txt (+100/-0)
uscards/install.sh (+9/-0)
uscards/scripts/sru-board (+158/-0)
uscards/scripts/uscoredev (+189/-0)
uscards/scripts/usdaily (+186/-0)
uscards/scripts/usmerges (+194/-0)
uscards/scripts/usmigration (+161/-0)
Reviewer Review Type Date Requested Status
Bryce Harrington (community) Approve
Review via email: mp+412268@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Bryce Harrington (bryce) wrote :

Guessing this MP is for c51ab9e only (which LGTM, +1).

However, one suggestion for the prior commit, 857c838; I was just some wget url code myself yesterday and saw a good tip on stack exchange that when downloading single files, curl seems better to use in scripts than wget.

Anyway, if you end up using migration-filter for something I'd be keen to hear about it.

Revision history for this message
Bryce Harrington (bryce) :
review: Approve
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Yeah just for that commit (merged it now) - thanks.

I can easily use curl, but I do not see a benefit of curl>wget do you have link handy why that would be "better"?

Finally migration-filter - I used "single-build-failures" to look for candidates to fix and realized to late it exists.
Otherwise excuses-top-blocker.sh might have become an extension to your tool.

If we'd be on feature requests, "at least older than" and "not older than" would be nice to add to filters. If too young then they are likely fixed by Debian, too old often is unfixable for some reason.

Revision history for this message
Bryce Harrington (bryce) wrote (last edit ):

I have a lot more experience with wget than curl, and have mostly used the former including in scripts. Guessing your experience has been similar.

wget has a definite advantage when you're downloading multiple files from a website, or doing things recursively. But for single-file downloads, the advantage I'm seeing on using curl is that it gives better low-level control and generally seems more robust when you're using in a scripted fashion. Curl seems to also be (slightly) faster, and provide more discrete error codes, which could be useful in a script.

Anyway, no biggie but just FYI that at least for my own purposes I'll probably be switching to curl over wget for scripted things myself.

Some links:
  * https://unix.stackexchange.com/questions/47434/what-is-the-difference-between-curl-and-wget
  * https://daniel.haxx.se/docs/curl-vs-wget.html

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/.gitignore b/.gitignore
0new file mode 1006440new file mode 100644
index 0000000..d32ac33
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
1/build/
2/dist/
3
4*.snap
5
6# Logs
7*-debug.log
8*-error.log
diff --git a/README.md b/README.md
0new file mode 1006449new file mode 100644
index 0000000..8ab8a50
--- /dev/null
+++ b/README.md
@@ -0,0 +1,218 @@
1# Ubuntu Server Tooling collection
2
3This is a collection of tools used by different team members of the
4Ubuntu server team. The purpose of this repository is to reduce the re-creation
5of such tools over and over again.
6
7We start with a per-user subdirectory as historically people have diverged
8the very same tools. The intention is to unify some things over time.
9
10All tools are as-is and without any guarantees.
11
12This file shall give an overview what these tools actually do to allow
13exploring and evaluating them more easily.
14
15## Proposed migration work
16
17* cpaelzer/check-autopkgtest-stats.sh
18Check the past of a packages autopkgtest success rate and scan for a given
19pattern in those fails. Most useful when debugging if a certain issue
20comes back but hiding between other errors.
21It will list which sub-test failed and visually show the frequency of those.
22Example: `$ check-autopkgtest-stats.sh -c 40 -p libreoffice -r xenial -a amd64 -P 'gb_JunitTest_DEBUGRUN'`
23will give you some stats plus `1 junit-subsequentcheck ( 2.50%) ....................F..................`
24
25* cpaelzer/check-component-mismatches.sh
26Run this against a link to a buildlog on launchpad - e.g. where you prep
27your shiny new package/version for main.
28It tries to identify dependencies of that build into non-main.
29
30* cpaelzer/check-component-mismatches-v2.sh
31Like the checker above, but instead of a buildlog just run it with a package
32name. Preferrably in a container with the target release.
33
34* cpaelzer/lp-affects-devel
35Mark a bug affected by a given package/release combo.
36Example: `$ lp-affects-devel 1863108 postgresql-9.5 xenial`
37
38* cpaelzer/lp-test-ppa
39Trigger and report autopkgtests on a PPA
40Example: `$ ./lp-test-ppa ppa:ci-train-ppa-service/4318 hirsute`
41
42* cpaelzer/rma
43Rmadison to Debian and Ubuntu at once to compare versions with each other.
44
45* cpaelzer/testify.sh
46Insert all autopkgtest architectures into a autopkgtest retry link
47
48* rbasak/chdist-if-migrated
49Simulate proposed migration for a set of packages so that you can then
50poke apt-get to see why britney says something will become
51uninstallable.
52
53## Package subscriptions by Teams
54
55* cpaelzer/get-packages-subscribed.py
56List packages a team is subscribed to (e.g. for MIR processing)
57
58* cpaelzer/check-OS-packages-on-server-mapping.sh
59Due to the team split in the past we have some overlap that we need to
60clear on a regular base. Finds packages subscribed by both teams.
61
62* cpaelzer/packageset-subscription-mismatches.sh
63Find packages subscribed but not in main (candiate to drop) and packages
64in server packageset (uploads) but not subscribed (candidate to clear either)
65
66## Updating Packages
67
68* cpaelzer/dput
69Break on common errors before uploading them
70
71* cpaelzer/git-to-dquilt.sh
72Convert a git commit into a debian/patches/ entry.
73Automatically adds dep3 headers, date and bug.
74Can work on multiple commits at once and trivializes bug-fixing.
75Note: you need to add the upstream remote beofre you can pick patches from
76there.
77Example:
78 git-to-dquilt.sh -b 1895366 -p "ubuntu-v3.0.3-fixes" -u 'https://github.com/corosync/corosync/commit/' 7f64a1dc ec889e89 9105d94a 21e1c711 ee38d93c f31a31f9
79
80* cpaelzer/sbuildchroot
81Enter an sbuild chroos without having to remember the /...../ path.
82
83* cpaelzer/localrepo.sh
84If you have a bunch of local debs e.g. from a sbuild run this will create
85and sign the necesary files to include it as a local repository.
86
87* cpaelzer/pull-uca-source.py
88Like pull-lp-source but for Ubuntu cloud archive
89
90## Virtualization stack special cases
91
92* cpaelzer/locallibvirt.sh
93Helps to build libvirt locally close to what we usually build for Ubuntu
94releases.
95
96* cpaelzer/buildpkg-qemu.sh
97Qemu had some serious mismatches between git and tarballs prior to Eoan.
98This helps to clean up before build and restore the proper content.
99
100## Packaging VCS management
101
102* rbasak/adopt.py
103Adopt non-VCS uploads made by other Ubuntu developers into your package
104maintenance VCS by making use of the synthesized git-ubuntu view.
105
106## Packaging/Container related tools:
107
108* bryce/ls-installed - dump list of what unique packages are installed
109* bryce/ls-lxc - list all lxc containers
110* bryce/rmad - look up debian and ubuntu versions of a package
111* bryce/dependency-tree - organize list of packages in revdep order
112* bryce/enable-debsrc - uncomments deb-src entries in sources.list*
113
114## Git branch review related tools:
115
116I don't find myself using these for Ubuntu packaging, but they proved
117handy when doing upstream maintenance work, esp. for reviewing large git
118branch MP's.
119
120* bryce/git-cmp-branches - diff branches
121* bryce/git-cmp-repos - diff entire repos
122* bryce/git-exam - dump git changes to a given file
123* bryce/git-grep - greps for a string within a range of commits
124* git-log-branch - pretty formatted log graph
125* bryce/git-pop - removes top commit (saved to /tmp/)
126
127## X11 / Desktop cli's:
128
129* bryce/middle-paste - I map this to ctrl+alt+v to simulate middle-paste
130
131## Basic Unix-style commands I've added to my workflow:
132
133* bryce/binit - Symlink a script into ~/bin, but keeps actual file in whatever
134 VCS I'm using.
135* bryce/columns - Extract columns from text tables. (I have a python version
136 of this but it's still a WIP.)
137* bryce/mv-smart - avoid clobbering existing files unless they're identical
138* bryce/prepend - Inserts file content at top of files
139* bryce/shuffle - Randomizes lines using python's random.shuffle()
140
141
142## Externally hosted things:
143
144* bryce/post-discourse: CLI tool for interacting with Discourse
145 - https://gitlab.com/bryceharrington/post-discourse
146
147* bryce/launchpad-gm-scripts: Firefox enhancements for bug report triaging and
148 other launchpad-related work.
149 - https://launchpad.net/launchpad-gm-scripts
150
151* bryce/php-ubuntu-tools: CLI for preparing for packageset transitions.
152 - https://code.launchpad.net/~php-ubuntu/+git/php-ubuntu-tools
153 - Currently pretty specific to PHP but could be repurposeable for
154 other kinds of transitions.
155 - Honestly is kind of a mess because transitions involve so many
156 different parts which change on a daily basis, so it's tough to
157 engineer a general solution.
158 - I'm thinking instead of trying to automate the full process, to
159 break it down into smaller helper scripts...
160
161## Misc
162
163* rbasak/standup_timer.py: used to time our standups.
164
165## Experiments / WIP:
166
167These are some side projects I own or help maintain, which aren't
168currently in an immediately useful state, but may be worth mentioning if
169others have similar needs/interests.
170
171* ppa-dev-tools: CLI for creating PPAs.
172 - Create, list, delete, etc. PPAs
173 - https://launchpad.net/ppa-dev-tools
174
175* bryce/diskwatch: Displays status of collection of hdd's, including their
176 serial numbers, bus location, raid details, etc. Doesn't handle a
177 wide enough breadth of hardware, so still rather WIPish.
178 - https://gitlab.com/bryceharrington/diskwatch
179
180* bryce/python-fstab: Python module for managing fstab contents.
181 - Originally written by Lars Wirzenius, but seems unmaintained now.
182 - My hope was to use something off-the-shelf but what I've found
183 unfortunately doesn't meet my requirements. This came closest.
184 - Note: I've ported it to python3 but still buggy, not quite ready
185 for prime time.
186 - https://code.launchpad.net/~bryce/python-fstab/+git/python-fstab
187
188* bryce/python-bullettree: Python module for managing Debian-changelog style
189 bullet lists (* / - / +). Super early in development.
190 - https://gitlab.com/bryceharrington/bullet-tree
191
192* bryce/usmerges: Email service to look for merge opportunities.
193 - Not posted publically yet
194 - Kind of an agglomeration of quick hacks; I want to rewrite it with
195 test cases, proper python modules, and a less organic structure.
196
197* bryce/usdaily: A CLI for interacting with the ubuntu server team's trello
198 board.
199 - I've got some POC code implemented, but nothing shareable yet.
200
201
202## Wishlist:
203
204These are ideas for tools that I'd like, but haven't found anywhere, and
205am considering creating some day, maybe.
206
207* bryce/excuses-kicker: Tool for mass-retargeting autopkgtest runs, similar in
208 concept to retry-autopkgtest-regressions, but in bash.
209 - Handles acquisition of credentials through SSO/2FA, but is pretty
210 hacky.
211 - Planning to rewrite this in python
212 - Planning to build on top of retry-autopkgtest-regressions
213
214* bryce/python-apt-sources: Python module for managing /etc/apt/sources.list
215 and sources.list.d/
216 - There are chunks of code that parse or write these. I need
217 something that can read *and* write, and that is packaged in a
218 discrete python module.
diff --git a/bryce/binit b/bryce/binit
0new file mode 100755219new file mode 100755
index 0000000..47bb24e
--- /dev/null
+++ b/bryce/binit
@@ -0,0 +1,17 @@
1#!/bin/bash
2
3for item in $* ; do
4 if [ ! -e ${item} ]; then
5 echo "Error: No such file '${item}'"
6 continue
7 fi
8 path=$(realpath ${item})
9 tool=$(basename ${path})
10 if [ -e ~/bin/$tool ]; then
11 echo "Error: $tool already in ~/bin"
12 continue
13 fi
14 ln -s $path ~/bin/$tool
15 echo "linked ~/bin/$tool"
16done
17
diff --git a/bryce/check-scripts b/bryce/check-scripts
0new file mode 10075518new file mode 100755
index 0000000..ef656f3
--- /dev/null
+++ b/bryce/check-scripts
@@ -0,0 +1,100 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4# Do some basic compile/lint style checks on scripts of various
5# programming languages.
6
7# Copyright (C) 2020 Bryce W. Harrington <bryce@bryceharrington.org>
8#
9# This program is free software: you can redistribute it and/or
10# modify it under the terms of the GNU Affero General Public
11# License as published by the Free Software Foundation, either
12# version 3 of the License, or (at your option) any later
13# version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23progname=$(basename "${0}")
24
25usage() {
26 cat >&2 <<EOF
27Usage: ${progname} [scripts]
28EOF
29 exit "${1}"
30}
31
32warn() {
33 echo "Warning: $1" 1>&2
34}
35
36die() {
37 echo "Error: $1" 1>&2
38 exit 1
39}
40
41function check_script_bash() {
42 local code_file="${1}"
43
44 bash -n "${code_file}"
45 shellcheck "${code_file}"
46}
47
48function check_script_python() {
49 local code_file="${1}"
50
51 python3 -m py_compile "${code_file}"
52 if [ -e "${code_file}c" ]; then
53 rm "${code_file}c"
54 fi
55 pyflakes3 "${code_file}"
56 pylint "${code_file}"
57}
58
59function check_code_c() {
60 local code_file="${1}"
61 die "${FUNCNAME[0]}: Unimplemented"
62}
63
64function check_code_cpp() {
65 local code_file="${1}"
66 die "${FUNCNAME[0]}: Unimplemented"
67}
68
69main() {
70 # TODO: If no args given, check all scripts in CWD
71
72 for code_file in "${@}"; do
73 code_file=$(realpath "${code_file}")
74 file_type=$(file -b "${code_file}")
75
76 case ${file_type,,} in
77 *bash*)
78 check_script_bash "${code_file}"
79 ;;
80 *bourne*)
81 check_script_bash "${code_file}"
82 ;;
83 *python*script*)
84 check_script_python "${code_file}"
85 ;;
86 *c++*source*)
87 check_code_cpp "${code_file}"
88 ;;
89 *c*source*)
90 check_code_c "${code_file}"
91 ;;
92 *)
93 warn "Unknown script type '${file_type}' for ${code_file}"
94 esac
95 done
96}
97
98if [[ "${0}" == "${BASH_SOURCE[0]}" ]]; then
99 main "$@"
100fi
diff --git a/bryce/cl-diff b/bryce/cl-diff
0new file mode 100755101new file mode 100755
index 0000000..f87b174
--- /dev/null
+++ b/bryce/cl-diff
@@ -0,0 +1,7 @@
1#!/usr/bin/env bash
2
3git diff debian/changelog \
4 | grep '^+ ' \
5 | sed 's/^../ /' \
6 | sed 's/ \* / - /' \
7 | sed 's/ \- / + /'
diff --git a/bryce/columns b/bryce/columns
0new file mode 1007558new file mode 100755
index 0000000..b36455f
--- /dev/null
+++ b/bryce/columns
@@ -0,0 +1,27 @@
1#!/usr/bin/perl -w
2# -*- coding: utf-8 -*-
3
4my @fields = grep { $_ =~ /^\d+\+?$/ } sort @ARGV;
5
6while (<STDIN>) {
7 chomp;
8 $_ =~ s/^\s+//;
9 my @cols = split /\s+/, $_;
10 my $show_after = undef;
11 foreach my $field (@fields) {
12 if ($field =~ /\+$/) {
13 $field =~ s/\+//;
14 $show_after = $field;
15 }
16 next unless $field && $cols[$field-1];
17 print $cols[$field-1], ' ';
18 }
19 if ($show_after) {
20 $field = $show_after;
21 while ($field < $#cols+1) {
22 $field += 1;
23 print $cols[$field-1], ' ';
24 }
25 }
26 print "\n";
27}
diff --git a/bryce/debb b/bryce/debb
0new file mode 10075528new file mode 100755
index 0000000..c16456e
--- /dev/null
+++ b/bryce/debb
@@ -0,0 +1,26 @@
1#!/bin/bash
2
3# This is a convenience script for Ubuntu packagers to submit bug
4# reports to Debian, skipping some unnecessary steps, or checks that are
5# inappropriate for packagers (such as whether the package is
6# installed).
7
8attachments=
9package="${1}"
10if [ -n "${2}" ]; then
11 attachments="-A ${2}"
12fi
13
14reportbug ${attachments} \
15 --bts debian \
16 --email "${DEBEMAIL}" \
17 --no-query-bts \
18 --no-check-available \
19 --no-check-installed \
20 --no-config-files \
21 --no-verify \
22 --mutt \
23 --source "${package}"
24
25# TODO: Provide version number as whatever the current debian version is
26# TODO: Allow linking to a launchpad bug report, that is being forwarded
diff --git a/bryce/debian-changes b/bryce/debian-changes
0new file mode 10075527new file mode 100755
index 0000000..0393d59
--- /dev/null
+++ b/bryce/debian-changes
@@ -0,0 +1,14 @@
1#!/bin/bash
2
3package="${1}"
4p="${package:0:1}"
5
6# TODO: This misses lib*
7# (I seem to recall seeing a script that handled this lookup logic...)
8
9wget -q -O- https://metadata.ftp-master.debian.org/changelogs//main/${p}/${package}/unstable_changelog | head -n 100 | less
10
11# TODO: Only show entries since whatever is in Ubuntu
12
13# TODO: Somehow provide way to view the diff for a given version
14# (seems like a job for git-ubuntu?)
diff --git a/bryce/dependency-tree b/bryce/dependency-tree
0new file mode 10075515new file mode 100755
index 0000000..ffb7012
--- /dev/null
+++ b/bryce/dependency-tree
@@ -0,0 +1,59 @@
1#!/bin/bash
2
3# Usage: cat my-packages.txt | dependency-tree
4#
5# - my-packages.txt could be generated via a command like:
6#
7# $ apt-cache policy "php-horde-*" | grep "^[a-z]" | cut -d: -f1 > my-packages.txt
8
9# Note: This only works for php-horde. To make it more generally usable
10# would need to give it a better way to filter dependencies (see TODO-1
11# and TODO-2).
12
13# Slurp in our package list from stdin
14unsorted_packages=
15while read pkg; do
16 # Also add the package's dependencies
17 # TODO-1: Need better way to extract the relevant rdepends without hardcoding "php-horde"
18 rdepends=$(apt-cache rdepends "${pkg}" --recurse \
19 --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances \
20 | grep "^ php-horde-" | grep -v php-horde-core | sort | uniq)
21 unsorted_packages="${unsorted_packages} ${pkg} ${rdepends}"
22done < "${1:-/dev/stdin}"
23
24# Sort ${package_list} to unique items
25package_list=$(echo -e "${unsorted_packages// /\\n}" | sort -u)
26
27seen_file=$(mktemp)
28level=0
29
30while [ -n "${package_list}" ]; do
31 echo "### Dependency Level ${level} ###"
32 remaining_packages=
33 for pkg in ${package_list}; do
34 apt-cache show "$pkg" 2>&1 | grep -q ^Package: || continue
35
36 # TODO-2: Ignore non-php-horde packages
37 depends=$(apt-cache rdepends "${pkg}" \
38 --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances \
39 | grep "^ php-horde-" | sort | uniq \
40 | grep -vwFf "${seen_file}")
41 if [ -z "${depends}" ]; then
42 echo "${pkg}"
43 echo "${pkg}" >> "${seen_file}"
44 else
45 remaining_packages="${remaining_packages} ${pkg}"
46 fi
47
48 done
49 if [ "${package_list}" = "${remaining_packages}" ]; then
50 echo
51 echo "### Error - Could not untangle dependencies: ###"
52 echo "${remaining_packages}"
53 exit 1
54 fi
55 package_list="${remaining_packages}"
56 level=$(( level + 1 ))
57 echo
58 echo
59done
diff --git a/bryce/enable-debsrc b/bryce/enable-debsrc
0new file mode 10075560new file mode 100755
index 0000000..eda532a
--- /dev/null
+++ b/bryce/enable-debsrc
@@ -0,0 +1,28 @@
1#!/usr/bin/env python3
2
3import os
4import sys
5from softwareproperties.SoftwareProperties import SoftwareProperties
6
7"""
8This script enables all deb-src entries in sources.list if the
9corresponding deb line has been enabled.
10"""
11
12if __name__ == "__main__":
13 sp = SoftwareProperties(options=None)
14 enabled_deb_entry = None
15 for entry in sp.sourceslist.list:
16 if entry.type == 'deb' and not entry.disabled:
17 enabled_deb_entry = entry
18 elif (entry.type == 'deb-src'
19 and entry.architectures == enabled_deb_entry.architectures
20 and entry.trusted == enabled_deb_entry.trusted
21 and entry.uri == enabled_deb_entry.uri
22 and entry.dist == enabled_deb_entry.dist
23 and entry.comps == enabled_deb_entry.comps):
24 entry.set_enabled(True)
25 sp.sourceslist.save()
26
27 os.execvp("apt-get", ["apt-get", "update"])
28 sys.exit(0)
diff --git a/bryce/enable-proposed b/bryce/enable-proposed
0new file mode 10075529new file mode 100755
index 0000000..e5f7006
--- /dev/null
+++ b/bryce/enable-proposed
@@ -0,0 +1,38 @@
1#!/usr/bin/env python3
2
3import os
4import sys
5import aptsources
6from softwareproperties.SoftwareProperties import SoftwareProperties
7
8"""
9This script enables -proposed in sources.list for the current distro.
10"""
11
12def main_archive_uri(sp):
13 for entry in sp.sourceslist.list:
14 if (entry.type == 'deb'
15 and not entry.invalid
16 and not entry.disabled
17 and entry.dist == sp.distro.codename
18 and entry.file == '/etc/apt/sources.list'
19 and 'main' in entry.comps
20 and 'ppa.launchpad.net' not in entry.line):
21 return entry.uri
22
23if __name__ == "__main__":
24 sp = SoftwareProperties(options=None)
25 distro = aptsources.distro.get_distro()
26 distro.get_sources(sp.sourceslist)
27 dist = '{}-proposed'.format(sp.distro.codename)
28 uri = main_archive_uri(sp)
29 comps = list(distro.enabled_comps)
30 for t in 'deb', 'deb-src':
31 if not sp.sourceslist.add(type=t, uri=uri, dist=dist, orig_comps=comps):
32 print(_("Error: '%s' invalid") % line)
33 sys.exit(1)
34
35 sp.sourceslist.save()
36
37 os.execvp("apt-get", ["apt-get", "update"])
38 sys.exit(0)
diff --git a/bryce/git-cmp-branches b/bryce/git-cmp-branches
0new file mode 10075539new file mode 100755
index 0000000..0f12e8c
--- /dev/null
+++ b/bryce/git-cmp-branches
@@ -0,0 +1,7 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4a="${1}"
5b="${2}"
6
7git log "${a}" "^${b}" --no-merges
diff --git a/bryce/git-cmp-repos b/bryce/git-cmp-repos
0new file mode 1007558new file mode 100755
index 0000000..f40bd69
--- /dev/null
+++ b/bryce/git-cmp-repos
@@ -0,0 +1,27 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4# Given two repositories, show the commits that differ
5
6a="${1}"
7b="${2}"
8
9cd "${a}"
10git log | grep "^commit " | cut -d' ' -f2 > /tmp/git-cmp-a.commits
11cd ..
12
13cd "${b}"
14i=0
15for commit in $(cat /tmp/git-cmp-a.commits); do
16 git show "${commit}" > /dev/null 2>&1
17 if [ $? != 0 ]; then
18 echo "${commit}"
19 id=$(printf "head-%03d_%s" "${i}" "${commit}")
20 cd ..
21 cd "${a}"
22 git show "${commit}" > "../${id}.patch"
23 cd ..
24 cd "${b}"
25 i=$(( i + 1 ))
26 fi
27done
diff --git a/bryce/git-exam b/bryce/git-exam
0new file mode 10075528new file mode 100755
index 0000000..83ccaf6
--- /dev/null
+++ b/bryce/git-exam
@@ -0,0 +1,45 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4# For a given file, review the past changes to just that file
5
6FILENAME="${1}"
7
8# lexer = diff
9# formatters = raw, svg, terminal, terminal256, text
10# filters = whitespace, highlight
11# styles = monokai, manni, borland, colorful, default, murphy, trac, tango, fruity, autumn, bw, emacs, vim, pastie, friendly, native
12
13## diff.py lexer:
14# (r' .*\n', Text),
15# (r'\+.*\n', Generic.Inserted),
16# (r'-.*\n', Generic.Deleted),
17# (r'!.*\n', Generic.Strong),
18# (r'@.*\n', Generic.Subheading),
19# (r'([Ii]ndex|diff).*\n', Generic.Heading),
20# (r'=.*\n', Generic.Heading),
21# (r'.*\n', Text),
22
23## terminal.py formatter:
24# Generic.Deleted: ('red', 'red'),
25# Generic.Inserted: ('darkgreen', 'green'),
26# Generic.Heading: ('**', '**'),
27# Generic.Subheading: ('*purple*', '*fuchsia*'),
28# Generic.Prompt: ('**', '**'),
29
30## native.py style:
31# Generic.Heading: 'bold #ffffff',
32# Generic.Subheading: 'underline #ffffff',
33# Generic.Deleted: '#d22323',
34# Generic.Inserted: '#589819',
35# Generic.Error: '#d22323',
36
37PYG_OPTS="-O style=gitdiff"
38
39for commit in $(git rev-list HEAD -- "${FILENAME}"); do
40 git show "${commit}" -- follow "${FILENAME}" | cat
41done | pygmentize "${PYG_OPTS}" -f terminal256 -l "diff" | less -QR
42
43#done | highlight --out-format=xterm256 --src-lang=diff -s moria | less -QR
44#done | source-highlight -s diff -f | less -QR
45
diff --git a/bryce/git-grep b/bryce/git-grep
0new file mode 10075546new file mode 100755
index 0000000..f9ae166
--- /dev/null
+++ b/bryce/git-grep
@@ -0,0 +1,35 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4# Greps for a string within a range of commits
5
6progname=$(basename ${0})
7
8usage() {
9 cat >&2 <<EOF
10Usage: ${progname} <pattern> <range>
11EOF
12}
13
14if [ "${1}" = "-h" ] || [ "${1}" = "--help" ]; then
15 usage
16 exit 0
17elif [[ ${#} -lt 2 ]]; then
18 usage
19 exit 1
20fi
21
22PATTERN="${1}"
23RANGE="${2}"
24
25for commit in $(git rev-list "${RANGE}"); do
26 matches=$(git show "${commit}" | grep "${PATTERN}")
27 if [ -z "${matches}" ]; then
28 continue
29 fi
30 echo
31 git show -s --oneline "${commit}" | cat
32 echo "$matches" | grep "${PATTERN}"
33 echo
34done
35
diff --git a/bryce/git-log-branch b/bryce/git-log-branch
0new file mode 10075536new file mode 100755
index 0000000..8f696c0
--- /dev/null
+++ b/bryce/git-log-branch
@@ -0,0 +1,4 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative master..HEAD
diff --git a/bryce/git-pop b/bryce/git-pop
0new file mode 1007555new file mode 100755
index 0000000..98b0c07
--- /dev/null
+++ b/bryce/git-pop
@@ -0,0 +1,24 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4progname=$(basename "${0}")
5
6usage() {
7 cat >&2 <<EOF
8Usage: ${progname}
9EOF
10}
11
12if [ "${1}" = "-h" ] || [ "${1}" = "--help" ]; then
13 usage
14 exit 0
15fi
16
17# TODO: Check if we're on a branch with no upstream defined,
18# and if so bail with an appropriate error. Otherwise,
19# the git reset will fail
20
21git format-patch -o /tmp master
22git reset --hard @{u}
23git status
24
diff --git a/bryce/gu-set-target b/bryce/gu-set-target
0new file mode 10075525new file mode 100755
index 0000000..2e53726
--- /dev/null
+++ b/bryce/gu-set-target
@@ -0,0 +1,108 @@
1#!/usr/bin/python3
2# -*- coding: utf-8 -*-
3
4# Makes the imported repository the default for that Ubuntu package in
5# Launchpad. This will allow git ubuntu clone to find the repository,
6# since it looks for the default.
7#
8# Process for handling an import request:
9# https://docs.google.com/document/d/1LimtZjV4ldeIK_IY9GngumacljhqRdiwIcgWlI1TFiY/edit#heading=h.x44nuoia544z
10
11from os.path import join
12import asyncio
13import tenacity
14import argparse
15from functools import lru_cache
16from launchpadlib.launchpad import Launchpad
17from xdg.BaseDirectory import xdg_config_home
18
19class Config:
20 HOME = xdg_config_home
21
22class Lp:
23 # Extraction of bileto's lp class.
24 # (See Pdbq for a more thorough implementation.)
25 _real_instance = None
26
27 def __init__(self, application_name, service=Launchpad, config=Config):
28 """Create a Launchpad service object."""
29 self._app_name = application_name
30 self._service = service
31 self._config = config
32
33 def _get_instance(self):
34 """Authenticate to Launchpad."""
35 return self._service.login_with(
36 application_name=self._app_name,
37 service_root='production',
38 allow_access_levels=['WRITE_PRIVATE'],
39 version='devel', # Need devel for copyPackage.
40 credentials_file=join(self._config.HOME, '.launchpad.credentials'),
41 )
42
43 @property
44 def _instance(self):
45 """Cache LP object."""
46 if not self._real_instance:
47 self._real_instance = self._get_instance()
48 return self._real_instance
49
50 @property
51 @lru_cache()
52 def _api_root(self):
53 """Identify the root URL of the launchpad API."""
54 return self._instance.resource_type_link.split('#')[0]
55
56 def __getattr__(self, attr):
57 """Wrap launchpadlib so tightly you can't tell the difference."""
58 assert not attr.startswith('_'), "Can't getattr for %s" %(attr)
59 instance = super(Lp, self).__getattribute__('_instance')
60 return getattr(instance, attr)
61
62
63def create_parser():
64 """Sets up the command line parser object.
65
66 :rtype: argparse.ArgumentParser
67 :returns: parser object, ready to run <parser>.parse_args().
68 """
69 parser = argparse.ArgumentParser(
70 description='Makes an imported repository the default in Launchpad',
71 )
72 parser.add_argument(
73 'packages',
74 nargs=argparse.REMAINDER)
75 return parser
76
77@tenacity.retry(
78 retry=tenacity.retry_if_exception_type(KeyError),
79 wait=tenacity.wait_fixed(300),
80 )
81async def set_git_ubuntu_default_target(pkg):
82 """
83 Sets the default target for the given package.
84
85 :param str pkg: Name of source package.
86 """
87 lp = Lp('git-ubuntu')
88 repository = lp.git_repositories.getByPath(
89 path=f'~git-ubuntu-import/ubuntu/+source/{pkg}',
90 )
91 if repository is None:
92 print(f"{pkg} does not exist yet. Waiting...")
93 raise KeyError(f"Repository for {pkg} not found")
94 lp.git_repositories.setDefaultRepository(
95 repository=repository,
96 target=lp.distributions['ubuntu'].getSourcePackage(name=pkg),
97 )
98 print(f"{pkg} set successfully")
99
100async def set_all_async_with_wait(pkgs):
101 await asyncio.gather(*[set_git_ubuntu_default_target(pkg) for pkg in pkgs])
102
103if __name__ == "__main__":
104 # Option handling
105 parser = create_parser()
106 args = parser.parse_args()
107
108 asyncio.run(set_all_async_with_wait(args.packages))
diff --git a/bryce/gu-whitelist b/bryce/gu-whitelist
0new file mode 100755109new file mode 100755
index 0000000..c0608f7
--- /dev/null
+++ b/bryce/gu-whitelist
@@ -0,0 +1,119 @@
1#!/bin/bash
2
3# Process for handling an import request:
4# https://docs.google.com/document/d/1LimtZjV4ldeIK_IY9GngumacljhqRdiwIcgWlI1TFiY/edit#heading=h.x44nuoia544z
5
6remote_user="${GU_REMOTE_USER:-ubuntu}"
7remote_host="${GU_REMOTE_HOST}"
8cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}"
9usd_git_repo="${GU_GIT_REPO:-$cache_dir/usd-importer-whitelist}"
10priority="${GU_PRIORITY:-5}"
11
12## Prints to stderr
13function info() {
14 echo "${@}" 1>&2
15}
16
17## Prints to stderr, prefixed by 'Error:'
18function error() {
19 echo "Error: ${@}" 1>&2
20}
21
22## Prints to stderr, prefixed by 'Fatal:', and terminates with non-zero exit code
23function die() {
24 echo "Fatal: ${@}" 1>&2
25 exit ${2:-1}
26}
27
28function remote_exec() {
29 local cmd="${1}"
30
31 # Send command via ssh to remote server
32 info "ssh ${remote_user}@${remote_host} ${cmd}"
33 ssh "${remote_user}@${remote_host}" "${cmd}"
34}
35
36# Verify the VPN is activated. If not, error & suggest enabling it.
37info "Checking if VPN is enabled"
38if ! ping -c1 "${remote_host}"; then
39 die "Could not ping ${remote_host}. Is VPN enabled?"
40fi
41
42# If repo doesn't exist, check it out
43if [ ! -d "${usd_git_repo}" ]; then
44 lp_user=$(git config gitubuntu.lpuser)
45 if [ $? != 0 ] || [ -z "${lp_user}" ]; then
46 die "Could not determine LP username (set via git config gitubuntu.lpuser=<username>)"
47 fi
48 git clone "git+ssh://${lp_user}@git.launchpad.net/usd-importer" "${usd_git_repo}" \
49 || die "Could not clone usd-importer from launchpad"
50elif [ ! -d "${usd_git_repo}/.git" ]; then
51 die "${usd_git_repo} already exists but is not a valid git repository"
52fi
53
54cd "${usd_git_repo}" \
55 || die "Could not chdir ${usd_git_repo}"
56
57# Check for any local changes
58if [ -n "$(git diff --name-only)" ]; then
59 die "There are uncommitted changes in ${usd_git_repo}"
60elif [ -n "$(git log origin/master..HEAD)" ]; then
61 die "There are unpushed changes in ${usd_git_repo}"
62fi
63
64git pull \
65 || die "Could not fetch from remote"
66
67# TODO: Add option to pull changes from a merge proposal. E.g.:
68# https://code.launchpad.net/~brian-murray/usd-importer/+git/usd-importer/+merge/406471
69
70# See if last entry appears to be from current user. If not, add comment
71# with attribution for the new entries.
72name=$(git config user.name)
73last_section=$(grep ^# gitubuntu/source-package-whitelist.txt | tail -n 1)
74if [ "${last_section}" != "Requested by ${name}" ]; then
75 echo "" >> gitubuntu/source-package-whitelist.txt
76 echo "# Requested by ${name}" >> gitubuntu/source-package-whitelist.txt
77fi
78
79num_added=0
80for package_name in ${@}; do
81 info "Whitelisting ${package_name}"
82 # Check if package is already in the whitelist
83 if grep "^${package_name}$" gitubuntu/source-package-whitelist.txt; then
84 info "+ ${package_name} is already in the whitelist."
85 else
86 # Append packages to whitelist
87 echo "${package_name}" >> gitubuntu/source-package-whitelist.txt
88 num_added=$(( num_added + 1 ))
89 fi
90done
91
92if [ ${num_added} -gt 0 ]; then
93 git commit -am "Add ${package_name} to the whitelist."
94 git show HEAD | cat
95 echo "Make any changes to HEAD locally, and commit."
96 echo "${usd_git_repo}"
97 echo -n "Push changes [yn]? "
98 read ANSWER
99 [ "${ANSWER}" = 'y' ] || exit 1
100 git push
101else
102 info "+ No packages needed added to whitelist"
103 # Discard the request comment
104 git checkout gitubuntu/source-package-whitelist.txt
105fi
106
107remote_exec 'cd ~ubuntu/live-whitelist-blacklist-source/ && git pull'
108remote_exec 'sudo systemctl --no-block restart git-ubuntu-importer-service-poller'
109
110for package_name in ${@}; do
111 info "Requesting ${package_name} import"
112 sql="INSERT INTO request (srcpkg, timestamp, priority) VALUES ('${package_name}', $(date +%s), ${priority})"
113 info "+ Executing: '${sql}'"
114 if ! remote_exec "sqlite3 /var/local/git-ubuntu/db \"${sql}\""; then
115 error "importing ${package_name}"
116 fi
117 sleep 1
118 info ""
119done
diff --git a/bryce/install-build-deps b/bryce/install-build-deps
0new file mode 100755120new file mode 100755
index 0000000..73a19f6
--- /dev/null
+++ b/bryce/install-build-deps
@@ -0,0 +1,11 @@
1#!/bin/bash
2
3sudo apt-get install -y equivs
4sudo mk-build-deps \
5 --install \
6 --remove \
7 --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' \
8 ./debian/control
9
10sudo rm -f *-build-deps_*_amd64.buildinfo
11sudo rm -f *-build-deps_*_amd64.changes
diff --git a/bryce/json-check b/bryce/json-check
0new file mode 10075512new file mode 100755
index 0000000..69e4a0f
--- /dev/null
+++ b/bryce/json-check
@@ -0,0 +1,21 @@
1#!/usr/bin/env python
2
3# Copyright (C) 2012 Bryce Harrington
4# License: http://www.opensource.org/licenses/mit-license.php
5
6import sys
7import json
8
9filename = sys.argv[1]
10s = open(filename).read()
11try:
12 j = json.loads(s)
13 errcode = 0
14 sys.stderr.write("PASS\n")
15except:
16 errcode = 1
17 sys.stderr.write("FAIL: Invalid json\n")
18 raise
19sys.exit(errcode)
20
21
diff --git a/bryce/json-get b/bryce/json-get
0new file mode 10075522new file mode 100755
index 0000000..c667a64
--- /dev/null
+++ b/bryce/json-get
@@ -0,0 +1,43 @@
1#!/usr/bin/python3
2
3import sys
4import json
5import pprint
6
7if len(sys.argv) < 3:
8 print("usage: json-get <json-filename> [ALL|<path.in.data.structure>]")
9 sys.exit(1)
10
11filename = sys.argv[1]
12json_path = sys.argv[2]
13
14with open(filename) as json_data:
15 data = json.load(json_data)
16
17path_elements = json_path.split('.')
18
19if json_path != "ALL":
20 for i in path_elements:
21 if type(data) is dict:
22 data = data.get(i, None)
23 elif type(data) is list:
24 if i.isdigit():
25 data = data[int(i)]
26 elif '=' in i:
27 (key,value) = i.split('=', 2)
28 found = False
29 for item in data:
30 if item[key] == value:
31 data = item
32 found = True
33 break
34 else:
35 print(type(i))
36 else:
37 print(type(data))
38
39if type(data) is list or type(data) is hash:
40 pp = pprint.PrettyPrinter()
41 pp.pprint(data)
42else:
43 print(data)
diff --git a/bryce/ls-installed b/bryce/ls-installed
0new file mode 10075544new file mode 100755
index 0000000..98737e0
--- /dev/null
+++ b/bryce/ls-installed
@@ -0,0 +1,3 @@
1#!/bin/bash
2
3dpkg --get-selections | cut -f 1
diff --git a/bryce/ls-lxc b/bryce/ls-lxc
0new file mode 1007554new file mode 100755
index 0000000..0b569ea
--- /dev/null
+++ b/bryce/ls-lxc
@@ -0,0 +1,3 @@
1#!/bin/bash
2
3lxc list -c ns4b | grep -v '^\+'
diff --git a/bryce/middle-paste b/bryce/middle-paste
0new file mode 1007554new file mode 100755
index 0000000..10786e2
--- /dev/null
+++ b/bryce/middle-paste
@@ -0,0 +1,14 @@
1#!/bin/bash
2
3## This works, but results in system getting
4#xdotool type --clearmodifiers "$(xsel)"
5
6xdotool mousedown --clearmodifiers 2
7xdotool mouseup 2
8xdotool keyup alt
9xdotool keyup ctrl
10xdotool keyup shift
11
12# --delay milliseconds
13
14
diff --git a/bryce/migration-filter b/bryce/migration-filter
0new file mode 10075515new file mode 100755
index 0000000..e772244
--- /dev/null
+++ b/bryce/migration-filter
@@ -0,0 +1,217 @@
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4# Author: Bryce Harrington <bryce@canonical.com>
5#
6# Copyright (C) 2020 Bryce W. Harrington
7#
8# Released under GNU AGPL or later, read the file 'LICENSE.AGPL' for
9# more information.
10
11import json
12import lzma
13import os
14import urllib.request
15import re
16import sys
17import yaml
18
19if '__file__' in globals():
20 sys.path.insert(0, os.path.realpath(
21 os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")))
22
23URL = 'https://people.canonical.com/~ubuntu-archive/proposed-migration/update_excuses.yaml'
24
25def strip_html(raw_html):
26 re_html = re.compile('<.*?>')
27 return re.sub(re_html, '', raw_html)
28
29def excuses_to_issues(excuses):
30 for issue in excuses:
31 # Exclude administrivia text
32 if (issue.startswith("Migration status for") or
33 issue.startswith("Issues preventing migration:") or
34 issue.startswith("Additional info:") or
35 issue.endswith("days old") or
36 issue.endswith("autopkgtest delayed")):
37 continue
38 yield issue
39
40def arches_for_issue(issue):
41 re_arch = re.compile(r'\&arch=([^\&]*)\&')
42 for item in issue.split(','):
43 m = re_arch.search(item)
44 if m:
45 yield m.group(1)
46
47def filter_only_test_complete(sources):
48 """Exclude sources that still have 'Test in progress' status."""
49 for source in sources:
50 in_progress = False
51 for issue in excuses_to_issues(source['excuses']):
52 if 'Test in progress' in issue:
53 in_progress = True
54 if not in_progress:
55 yield source
56
57def filter_only_build_failures(sources):
58 """Exclude sources that have issues other than build failures."""
59 for source in sources:
60 has_build_issue = False
61 has_non_build_issue = False
62 source['suggested-actions'] = []
63 for issue in excuses_to_issues(source['excuses']):
64 # Valid build issues
65 if issue.startswith("missing build on"):
66 has_build_issue = True
67 url = issue.split('"')[1]
68 arch = url.split('/')[-1]
69 source['suggested-actions'].append({
70 'command': None,
71 'description': "Retry build for {}".format(arch),
72 'url': url
73 })
74
75 # Other sorts of issues
76 else:
77 has_non_build_issue = True
78 if has_build_issue and not has_non_build_issue:
79 yield source
80
81def filter_only_autopkgtest_failures(sources):
82 """Exclude sources that have issues other than autopkgtest regressions"""
83 for source in sources:
84 has_autopkgtest_issue = False
85 has_non_autopkgtest_issue = False
86 source['suggested-actions'] = []
87 for issue in excuses_to_issues(source['excuses']):
88 # Valid test regressions
89 if issue.startswith("autopkgtest for"):
90 package = issue.split(' ')[2].rstrip(':').split('/')[0]
91
92 regressions = ' '.join(issue.split(' ')[3:])
93 for r in regressions.split(', '):
94 test_link_html = r.split(': ')[0].rstrip(': ')
95 test_url = test_link_html.split('"')[1]
96 test_arch = strip_html(test_link_html)
97
98 test_status_html = r.split(': ', 1)[1]
99 test_status_url = test_status_html.split('"')[1]
100 test_status = strip_html(test_status_html)
101
102 if test_status.startswith('Regression'):
103 source['suggested-actions'].append({
104 'command': "excuses-kicker --architecture {} {}".format(test_arch, package),
105 'description': "Retry test(s) for {} on {}".format(package, test_arch),
106 'arch': test_arch,
107 'url': test_url,
108 'status': test_status
109 })
110 has_autopkgtest_issue = True
111
112 # Other sorts of issues
113 else:
114 has_non_autopkgtest_issue = True
115 if has_autopkgtest_issue and not has_non_autopkgtest_issue:
116 yield source
117
118def filter_single_issue(sources):
119 """Excludes sources that have more than one issue listed."""
120 for source in sources:
121 num_issues = 0
122 for issue in excuses_to_issues(source['excuses']):
123 num_issues += 1
124 if num_issues > 1:
125 break
126 if num_issues == 1:
127 yield source
128
129def filter_single_arch(sources):
130 """Excludes sources that affect more than a single architecture."""
131 arch_sources = []
132 for source in sources:
133 arch = None
134 for issue in excuses_to_issues(source['excuses']):
135 arches = list(arches_for_issue(issue))
136 if arch is None and len(arches) == 1:
137 arch = arches[0]
138 arch_sources.append(source)
139 elif arches == [arch]:
140 arch_sources.append(source)
141
142 return arch_sources
143
144def main():
145 import sys
146 import argparse
147
148 # Option handling
149 parser = argparse.ArgumentParser(description='Query subsets of the proposed migration update excuses')
150 parser.add_argument(
151 '-j', '--json',
152 action='store_true',
153 help='Output a JSON data file',
154 default=False
155 )
156 parser.add_argument(
157 '-T', '--total',
158 action='store_true',
159 help='Print the total count of source packages satisfying filter criteria',
160 default=False
161 )
162 parser.add_argument(
163 'filter', nargs=argparse.REMAINDER,
164 help='Name of the filter to run'
165 )
166 args = parser.parse_args()
167
168 # TODO: Load from cached JSON file (query-
169 with urllib.request.urlopen(URL) as f:
170 # Use C implementation of the SafeLoader, which is faster for
171 # large files like this one.
172 migrations = yaml.load(lzma.decompress(f.read()), Loader=yaml.CSafeLoader)
173
174 generation_timestamp = migrations['generated-date']
175 sources = migrations['sources']
176
177 results = []
178 if 'all' in args.filter or args.filter == []:
179 results = sources
180 elif 'single-build-failures' in args.filter:
181 results = list(
182 filter_single_issue(
183 filter_only_build_failures(sources)
184 )
185 )
186 elif 'single-test-regressions' in args.filter:
187 results = list(
188 filter_single_arch(
189 filter_single_issue(
190 filter_only_autopkgtest_failures(
191 filter_only_test_complete(sources)
192 )
193 )
194 )
195 )
196 else:
197 print("Error: Unknown filters")
198 sys.exit(1)
199
200 if args.json:
201 print(json.dumps(
202 results,
203 sort_keys=True,
204 indent=4,
205 ))
206 elif args.total:
207 print("{} update excuse records found".format(len(results)))
208 else:
209 for result in results:
210 print("* {}:".format(result.get('item-name')))
211 for action in result.get('suggested-actions', []):
212 print(" - {}".format(action.get('description')))
213 print(" {}".format(action.get('url')))
214 print(" $ {}".format(action.get('command')))
215
216if __name__ == '__main__':
217 main()
diff --git a/bryce/monday-triage b/bryce/monday-triage
0new file mode 100755218new file mode 100755
index 0000000..248489c
--- /dev/null
+++ b/bryce/monday-triage
@@ -0,0 +1,14 @@
1#!/bin/bash
2
3triagers=(paelzer bryce paride utkarsh rbasak lucas sergio)
4num_triagers=7
5week=$(($(date +%V) + 1))
6week=${week##0}
7week=${week##+(0)}
8current=$((week % num_triagers))
9echo "Current: " ${triagers[${current}]}
10echo "Next week: " ${triagers[$(( (current + 1) % num_triagers))]}
11echo "2 weeks: " ${triagers[$(( (current + 2) % num_triagers))]}
12echo "3 weeks: " ${triagers[$(( (current + 3) % num_triagers))]}
13echo "4 weeks: " ${triagers[$(( (current + 4) % num_triagers))]}
14echo "5 weeks: " ${triagers[$(( (current + 5) % num_triagers))]}
diff --git a/bryce/mv-smart b/bryce/mv-smart
0new file mode 10075515new file mode 100755
index 0000000..f1cc987
--- /dev/null
+++ b/bryce/mv-smart
@@ -0,0 +1,52 @@
1#!/bin/bash
2
3progname=$(basename $0)
4
5usage() {
6 cat >&2 <<EOF
7Usage: ${progname} <file> [file [...]] <dest>
8
9Options:
10 -h, --help This help listing
11EOF
12}
13
14if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
15 usage
16 exit 0
17elif [[ $# -lt 2 ]]; then
18 usage
19 exit 1
20fi
21
22
23DEST=${@: -1}
24FILES=${@:1:$(($#-1))}
25
26for file_path in ${FILES} ; do
27 file=$(basename $file_path)
28 dest_file=${DEST}/${file}
29 if [ ! -e ${dest_file} ]; then
30 echo ${file}
31 mv -i ${file_path} ${dest_file}
32 continue
33 fi
34
35 if [ -d "${file_path}" ]; then
36 echo "directory ${file_path} already exists"
37 continue
38 fi
39
40 a=$(md5sum $file_path | cut -d' ' -f1)
41 b=$(md5sum $dest_file | cut -d' ' -f1)
42
43 if [ "$a" = "$b" ]; then
44 # files are the same; keep destination copy
45 #echo "$file is already in $DEST - $a = $b"
46 rm $file_path
47 else
48 echo "$dest_file exists and differs from $file. Skipping."
49 fi
50done
51
52# for dir in *; do diff --brief --recursive --no-dereference --new-file --no-ignore-file-name-case ~/src/$dir $dir > /dev/null 2>&1; if [ $? == 0 ]; then echo $dir; fi; done
diff --git a/bryce/pkg-clean b/bryce/pkg-clean
0new file mode 10075553new file mode 100755
index 0000000..81a487f
--- /dev/null
+++ b/bryce/pkg-clean
@@ -0,0 +1,36 @@
1#!/bin/bash
2
3# This script removes uncommitted changes from a checked out git repository.
4
5INTERACTIVE=${INTERACTIVE:-"no"}
6
7# TODO: Check for any changes in debian/patches/*
8# Check for changes to debian/control or debian/changelog
9deb_changes=$(git diff --name-only debian/changelog debian/control*)
10if [ -n "${deb_changes}" ]; then
11 if [ "${INTERACTIVE}" = "yes" ]; then
12 while true; do
13 read -p "Discard uncommitted changes? (Y/n)" answer
14 case "${answer}" in
15 [Yy]* )
16 echo "Discarding changes to ${deb_changes}" ;;
17 [Nn]* )
18 exit 1 ;;
19 * )
20 echo "Please answer yes or no." \\
21 esac
22 done
23 else
24 # TODO: Allow --force
25 echo "Uncommitted changes exist"
26 echo "${deb_changes}"
27 exit 1
28 fi
29fi
30
31git checkout .
32git clean -fxd
33
34# TODO: maybe use -e to exclude stuff?
35
36# TODO: Maybe git restore or git reset?
diff --git a/bryce/pkg-component b/bryce/pkg-component
0new file mode 10075537new file mode 100755
index 0000000..9cf91f5
--- /dev/null
+++ b/bryce/pkg-component
@@ -0,0 +1,37 @@
1#!/bin/bash
2
3# Given a package name, print what component it's in
4show_binary() {
5 local binary_package="${1}"
6
7 echo -n "${binary_package}: "
8 section=$(apt-cache show "${binary_package}" \
9 | grep Section: \
10 | cut -d: -f2)
11 if [[ "${section}" =~ "/" ]]; then
12 echo "${section}" \
13 | cut -d/ -f1 \
14 | sort -u \
15 | xargs echo
16 else
17 echo "main"
18 fi
19}
20
21for pkg in "${@}"; do
22 if apt-cache show "${pkg}" 2>&1 | grep Package: > /dev/null; then
23 show_binary "${pkg}"
24 continue
25 fi
26
27 pkg_details=$(apt-cache showsrc "${pkg}")
28 if [ -z "${pkg_details}" ]; then
29 echo " - Could not find a component for ${pkg}"
30 exit 1
31 fi
32
33 binaries=$(echo "${pkg_details}" | grep Binary: | cut -d: -f2 | sed -e "s/,//g")
34 for binary_package in ${binaries}; do
35 show_binary "${binary_package}"
36 done
37done
diff --git a/bryce/pkg-depends b/bryce/pkg-depends
0new file mode 10075538new file mode 100755
index 0000000..4bf4cd1
--- /dev/null
+++ b/bryce/pkg-depends
@@ -0,0 +1,39 @@
1#!/bin/bash
2
3# Looks up the install-time dependencies for the given package
4
5list_package_depends() {
6 binary_package="${1}"
7 apt-cache depends "${binary_package}" \
8 --recurse --no-recommends --no-suggests --no-conflicts \
9 --no-breaks --no-replaces --no-enhances \
10 | grep "^\w" \
11 | sort -u
12}
13
14if [ $# -ge 1 ]; then
15 input="printf %s\n ${@}"
16else
17 input="cat -"
18fi
19
20# TODO: read -r disables backslashes
21# See https://unix.stackexchange.com/questions/192786/what-is-the-meaning-of-read-r
22while read requested_package; do
23 echo "${requested_package}"
24
25 # Find all dependencies recursively
26 for depend in $(list_package_depends "${requested_package}"); do
27 # Don't repeat our own package
28 if [ "${depend}" = "${requested_package}" ]; then
29 continue
30 fi
31
32 # Skip things in main
33 component=$(pkg-component "${depend}")
34 if [ "${component}" = "main" ]; then
35 continue
36 fi
37 echo " ${depend}"
38 done
39done < <(${input})
diff --git a/bryce/pkg-nochange b/bryce/pkg-nochange
0new file mode 10075540new file mode 100755
index 0000000..61d98fe
--- /dev/null
+++ b/bryce/pkg-nochange
@@ -0,0 +1,177 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4# Easy tool for quickly creating no-change rebuild packages
5
6# No-change rebuilds are necessary when one of the package's
7# dependencies has been updated to a new ABI level.
8
9progname=$(basename "${0}")
10
11rationale="${RATIONALE:-No change rebuild}"
12codename="${CODENAME:-$(distro-info --devel)}"
13
14usage() {
15 cat >&2 <<EOF
16Usage: ${progname} [package-name] [changelog rationale]
17
18Options:
19 -h, --help This help text.
20EOF
21 exit "${1}"
22}
23
24## Prints given message to stderr if DEBUG is enabled
25##
26## @param str 1: String to be displayed.
27dbg() {
28 [ -n "${DEBUG}" ] && echo "${@}" 1>&2
29}
30
31## Prints to stderr
32##
33## @param str 1: String to be displayed.
34info() {
35 echo "${@}" 1>&2
36}
37
38## Prints to stderr, prefixed by 'Warning:'
39##
40## @param str 1: String to be displayed.
41warn() {
42 echo "Warning: ${@}" 1>&2
43}
44
45## Prints to stderr, prefixed by 'Error:'
46##
47## @param str 1: String to be displayed.
48error() {
49 echo "Error: ${@}" 1>&2
50}
51
52## Prints to stderr, prefixed by 'Fatal:', and terminates with non-zero exit code
53##
54## @param str 1: String to be displayed.
55## @param str 2: Exit error code.
56die() {
57 echo "Fatal: ${@}" 1>&2
58 exit ${2:-1}
59}
60
61## Strip leading and trailing whitespace
62trim() {
63 local trimmed
64
65 trimmed="${@}"
66 if [ -z "${trimmed}" ]; then
67 read trimmed
68 fi
69
70 # Strip leading spaces.
71 while [[ ${trimmed} == ' '* ]]; do
72 trimmed="${trimmed## }"
73 done
74
75 # Strip trailing spaces.
76 while [[ ${trimmed} == *' ' ]]; do
77 trimmed="${trimmed%% }"
78 done
79
80 echo "${trimmed}"
81}
82
83get_pkgsys() {
84 if [ -d ".git" ]; then
85 if git remote -v | grep -P "^pkg\t"; then
86 echo "git-ubuntu"
87 return 0
88 fi
89 fi
90 echo "apt"
91 return 0
92}
93
94get_version_from_changelog() {
95 dpkg-parsechangelog | grep ^Version | cut -d: -f2 | trim
96}
97
98get_upstream_version_from_changelog() {
99 get_version_from_changelog "${@}" | cut -d- -f1 | trim
100}
101
102install_build_dependencies() {
103 local package_name="${1}"
104
105 # Install build dependencies for package
106 if which install-build-deps; then
107 install-build-deps || return 1
108 elif [ -n "${package_name}" ]; then
109 sudo apt-get -y build-dep "${package_name}" || return 1
110 else
111 return 1
112 fi
113
114 return 0
115}
116
117rev_package_for_nochange() {
118 local codename="${1}"
119 local rationale="${2}"
120
121 echo "version: $(get_version_from_changelog)"
122 if [[ "$(get_version_from_changelog)" == *"ubuntu"* ]]; then
123 # If current version has ubuntuN, then just increment it.
124 dch -i --distribution "${codename}" "${rationale}"
125 else
126 # append build1
127 dch -l "build" --distribution "${codename}" "${rationale}"
128 fi
129}
130
131main() {
132 # Determine package name
133 if [ -d './debian' ]; then
134 # We're already in a source package tree, so use it preferentially
135 package_name=$(dpkg-parsechangelog | grep ^Source: | cut -d' ' -f2)
136 elif [ -n "${1}" ] || [ -n "${PACKAGE}" ]; then
137 # Package was specified on commandline
138 package_name="${1:-${PACKAGE}}"
139 package_dir=$(pkg-get "${package_name}") || {
140 warn "Could not get package source"
141 exit 1
142 }
143 cd "${package_dir}" || {
144 warn "Could not chdir '${package_dir}'"
145 exit 1
146 }
147 else
148 usage 1
149 fi
150
151 install_build_dependencies "${package_name}" || {
152 warn "Could not install build dependencies"
153 }
154
155 rev_package_for_nochange "${codename}" "${rationale}" || {
156 warn "Could not rev package version"
157 }
158
159 if [ "$(get_pkgsys)" = "git-ubuntu" ]; then
160 warn "tbd"
161 # TODO: If git-ubuntu, use push-for-upload
162 debuild -S
163 else
164 debuild -S
165 fi
166
167 # TODO: Look at pitt job for code to detect the right source.changes
168
169 ls -l ../*source.changes
170
171 info "Ready to upload:"
172 info " \$ dput ubuntu ../<whatever>source.changes"
173}
174
175if [[ "${0}" == "${BASH_SOURCE[0]}" ]]; then
176 main "${@}"
177fi
diff --git a/bryce/pkg-rdepends b/bryce/pkg-rdepends
0new file mode 100755178new file mode 100755
index 0000000..25ba63f
--- /dev/null
+++ b/bryce/pkg-rdepends
@@ -0,0 +1,9 @@
1#!/bin/bash
2
3# See also cpaelzer's versioned-rdepends.sh
4
5# TODO: --recurse?
6# TODO: --recommends?
7apt-cache --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances rdepends "${@}"
8
9# TODO: This should list whether the rdep is a Recommends or Depends
diff --git a/bryce/pkg-source b/bryce/pkg-source
0new file mode 10075510new file mode 100755
index 0000000..2385e22
--- /dev/null
+++ b/bryce/pkg-source
@@ -0,0 +1,27 @@
1#!/bin/bash
2
3for pkg in "${@}"; do
4 if ! pkg_details=$(apt-cache show "${pkg}"); then
5 if ! pkg_details=$(apt-cache showsrc "${pkg}"); then
6 echo " - Can't find ${pkg} in archive"
7 continue
8 fi
9 fi
10
11 if [ $? -ne 0 ]; then
12 echo " - Could not find source for ${pkg}"
13 exit 1
14 fi
15 source_package=$(echo "${pkg_details}" \
16 | grep Source: \
17 | cut -d: -f2 \
18 | sort -u \
19 | xargs echo)
20 if [ -z "${source_package}" ]; then
21 echo "Couldn't find source for ${pkg}"
22 echo "${pkg_details}"
23 else
24 echo "${source_package}"
25 fi
26
27done
diff --git a/bryce/prepend b/bryce/prepend
0new file mode 10075528new file mode 100755
index 0000000..abdc1e1
--- /dev/null
+++ b/bryce/prepend
@@ -0,0 +1,26 @@
1#!/bin/bash
2
3progname=$(basename $0)
4
5usage() {
6 cat >&2 <<EOF
7Usage: ${progname} <insert-file> <target-file>
8EOF
9}
10
11if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
12 usage
13 exit 0
14elif [[ $# -lt 2 ]]; then
15 usage
16 exit 1
17fi
18
19insert_file=$1
20target_file=$2
21dummy=/tmp/${target_file}.dummy
22
23cat $target_file > $dummy
24mv ${target_file} ${target_file}.old
25cat $insert_file $dummy > $target_file
26
diff --git a/bryce/rmad b/bryce/rmad
0new file mode 10075527new file mode 100755
index 0000000..d689b18
--- /dev/null
+++ b/bryce/rmad
@@ -0,0 +1,16 @@
1#!/bin/bash
2
3package_name="${1}"
4
5if [ -z "${package_name}" ]; then
6 # TODO: Is the directory we're in a valid package name?
7 echo "Usage: rmad <package-name>"
8 exit 1
9fi
10stable=$(distro-info --stable)
11codename=$(distro-info --devel 2>/dev/null)
12rmadison -s "${stable},${stable}-security,${stable}-updates,${codename},${codename}-proposed" -a "source" "${package_name}" | sed -e "s/ | source//"
13echo
14rmadison -u "debian" -a "source" "${package_name}" | grep -v oldstable | grep -v "\-(backports|debug)" | sed -e "s/ | source//"
15
16# Filter out *stable* and *testing*
diff --git a/bryce/shuffle b/bryce/shuffle
0new file mode 10075517new file mode 100755
index 0000000..6ad1453
--- /dev/null
+++ b/bryce/shuffle
@@ -0,0 +1,11 @@
1#!/usr/bin/env python3
2
3import sys
4import random
5
6filename = sys.argv[1]
7a = open(filename).read().strip().split("\n")
8
9random.shuffle(a)
10for i in a:
11 print(i)
diff --git a/check-before-lib-update.sh b/check-before-lib-update.sh
0deleted file mode 10075512deleted file mode 100755
index 971496c..0000000
--- a/check-before-lib-update.sh
+++ /dev/null
@@ -1,57 +0,0 @@
1#!/bin/bash
2set -ue
3
4if [ $# -ne 2 ];then
5 echo "invalid number of arguments"
6 exit 1
7fi
8REL=${1}
9PKG=${2}
10
11TDIR=$(mktemp -d)
12
13getchdist () {
14 local name=${1}
15 chdist --data-dir=${TDIR} create ${REL}-${name} 1>/dev/null
16 cat << EOF > ${TDIR}/${REL}-${name}/etc/apt/sources.list
17deb http://de.archive.ubuntu.com/ubuntu/ ${REL} main restricted universe multiverse
18deb-src http://de.archive.ubuntu.com/ubuntu/ ${REL} main restricted universe multiverse
19deb http://de.archive.ubuntu.com/ubuntu/ ${REL}-updates main restricted universe multiverse
20deb-src http://de.archive.ubuntu.com/ubuntu/ ${REL}-updates main restricted universe multiverse
21deb http://de.archive.ubuntu.com/ubuntu/ ${REL}-backports main restricted universe multiverse
22deb-src http://de.archive.ubuntu.com/ubuntu/ ${REL}-backports main restricted universe multiverse
23
24deb http://de.archive.ubuntu.com/ubuntu/ ${REL}-proposed main restricted universe multiverse
25deb-src http://de.archive.ubuntu.com/ubuntu/ ${REL}-proposed main restricted universe multiverse
26
27deb-src http://archive.canonical.com/ubuntu ${REL} partner
28
29deb http://security.ubuntu.com/ubuntu ${REL}-security main restricted
30deb-src http://security.ubuntu.com/ubuntu ${REL}-security main restricted
31deb http://security.ubuntu.com/ubuntu ${REL}-security universe
32deb-src http://security.ubuntu.com/ubuntu ${REL}-security universe
33deb http://security.ubuntu.com/ubuntu ${REL}-security multiverse
34deb-src http://security.ubuntu.com/ubuntu ${REL}-security multiverse
35EOF
36}
37
38getchdist "proposed-only"
39sed -i -e '/proposed/!d' ${TDIR}/${REL}-proposed-only/etc/apt/sources.list
40getchdist "proposed"
41
42chdist --data-dir=${TDIR} apt ${REL}-proposed-only update 1>/dev/null
43chdist --data-dir=${TDIR} apt ${REL}-proposed update 1>/dev/null
44
45chdist --data-dir=${TDIR} apt-cache ${REL}-proposed rdepends ${PKG} > ${TDIR}/rdepends
46cat ${TDIR}/rdepends |xargs -n1 chdist --data-dir=${TDIR} bin2src ${REL}-proposed | sort | uniq > ${TDIR}/rdepends-src
47cat ${TDIR}/rdepends-src |xargs -n1 chdist --data-dir=${TDIR} apt-cache ${REL}-proposed-only showsrc 2>/dev/null | grep-dctrl -nsPackage . > ${TDIR}/overlap
48
49ls -ltr ${TDIR}
50
51printf "\n\nYou might need to rebuild (*currently in proposed)\n"
52while read -r line; do
53 if grep -q -e "^$line$" ${TDIR}/overlap; then
54 printf "* "
55 fi
56 echo "$line"
57done < ${TDIR}/rdepends-src
diff --git a/cpaelzer/TrelloToJira.py b/cpaelzer/TrelloToJira.py
58new file mode 1007550new file mode 100755
index 0000000..9267d9b
--- /dev/null
+++ b/cpaelzer/TrelloToJira.py
@@ -0,0 +1,75 @@
1#!/usr/bin/env python3
2"""
3Convert trello (CSV) Data to Jira entries
4
5One might wonder that there is no better way, but even the pro products
6that exist seem more complex. If you just want to migrate a bunch of cards
7this appeared to be the most simple way and worked quite well.
8
9It should be a good start to derive other solutions from, if needed.
10"""
11
12import argparse
13import csv
14from jira import JIRA
15
16def main(filename, token, accountid, tag, user, server):
17 jiracon = JIRA(basic_auth=(user, token), server=server)
18
19 with open(filename, "r", newline='', encoding="utf-8") as csvfile:
20 reader = csv.reader(csvfile, delimiter=',', quotechar='"')
21 for row in reader:
22
23 # Header of the card
24 summary = row[1]
25 # Body of the card
26 description = row[3]
27
28 archived = row[18]
29 if archived == "true":
30 print("Skip Archived '%s'" % summary)
31 continue
32
33 new_tags = []
34 new_tags.append(tag)
35 # add any special tag conditions here to the list - you can process
36 # tags = row[4]
37 # which is the lost of tags from trello
38
39 print("Create %s with tags '%s'" % (summary, new_tags))
40
41 new_issue = jiracon.create_issue(project='SD',
42 summary=summary,
43 description=description,
44 issuetype={'name': 'Story'})
45 for new_tag in new_tags:
46 new_issue.fields.labels.append(new_tag)
47 new_issue.update(fields={"labels": new_issue.fields.labels})
48
49 print(" Done - new ID %s" % (new_issue))
50
51
52if __name__ == '__main__':
53 PARSER = argparse.ArgumentParser()
54 # Trello CSV export is rather readable and static
55 # Worst case one can easily manually edit it (mcuh easier than the json).
56 # json is more complete as it has all the object hierarchy and even history
57 # but that also makes it hard to process for simple use cases.
58 # Get it via "Show Menu" - "more" - "Print and export" - "export as CSV"
59 PARSER.add_argument('filename',
60 help='CVS File to read data from (exported from Trello)')
61 PARSER.add_argument('--token',
62 help='Token from https://id.atlassian.com/manage-profile/security/api-tokens')
63 PARSER.add_argument('--accountid',
64 help='ID of the person to assign items, fetched to from https://warthogs.atlassian.net/rest/api/3/user/search?query=<email>')
65 PARSER.add_argument('--tag',
66 help='A tag to be able to differentiate these from the many other items in the backlog')
67 PARSER.add_argument('--user',
68 help='Username (email) to log into Jira')
69 PARSER.add_argument('--server',
70 help='Jira Server to connect to',
71 default='https://warthogs.atlassian.net')
72 ARGS = PARSER.parse_args()
73
74 main(ARGS.filename, ARGS.token, ARGS.accountid, ARGS.tag, ARGS.user,
75 ARGS.server)
diff --git a/cpaelzer/autopkgtests-running.sh b/cpaelzer/autopkgtests-running.sh
0new file mode 10075576new file mode 100755
index 0000000..12877aa
--- /dev/null
+++ b/cpaelzer/autopkgtests-running.sh
@@ -0,0 +1,6 @@
1#!/bin/bash
2wget -O - -q http://autopkgtest.ubuntu.com/running \
3 | grep -e '<tr><th>Architecture:</th><td>.*</td></tr>' - \
4 | sed 's/<tr><th>Architecture:<\/th><td>//' \
5 | sed 's/<\/td><\/tr>//' \
6 | sort | uniq -c | sort -rn
diff --git a/cpaelzer/build-for-ppa.sh b/cpaelzer/build-for-ppa.sh
0new file mode 1007557new file mode 100755
index 0000000..fb7f15e
--- /dev/null
+++ b/cpaelzer/build-for-ppa.sh
@@ -0,0 +1,30 @@
1#!/bin/bash
2# linked as `bp` in ~/bin/ for quick access
3set -uxe
4
5cver=$(dpkg-parsechangelog --show-field Version)
6crel=$(dpkg-parsechangelog --show-field Distribution)
7clbckp=$(mktemp)
8cp debian/changelog "${clbckp}"
9
10if [[ "${cver}" =~ "ppa" ]]; then
11 # already has a ppa string, only increment
12 dch -i --preserve "PPA build"
13else
14 # find non existing ppa suffix
15 for i in {1..100}; do
16 newver="${cver}~${crel}ppa${i}"
17 nonepoch=$(echo "${newver}" | sed 's/[0-9]://')
18 if ! ls ../*${nonepoch}* 1>/dev/null 2>&1; then
19 break
20 fi
21 done
22 sed -i -e "1 s/ (.*) / (${newver}) /" debian/changelog
23fi
24
25# build source
26dpkg-buildpackage -S -nc -d -sa
27
28# Restore/Clean
29cp "${clbckp}" debian/changelog
30rm -f debian/files
diff --git a/buildpkg-qemu.sh b/cpaelzer/buildpkg-qemu.sh
0similarity index 96%31similarity index 96%
1rename from buildpkg-qemu.sh32rename from buildpkg-qemu.sh
2rename to cpaelzer/buildpkg-qemu.sh33rename to cpaelzer/buildpkg-qemu.sh
index c9f3d10..9c6772f 100755
--- a/buildpkg-qemu.sh
+++ b/cpaelzer/buildpkg-qemu.sh
@@ -37,5 +37,9 @@ rm -f debian/control
37./debian/rules debian/control37./debian/rules debian/control
38chmod +w debian/control38chmod +w debian/control
3939
40# Old qemu had empty dirs, but that will end up in the tarball and
41# complain about mismatches
42find -name ..git -exec rm -f {} \;
43
40# Build pkg and add any extra arguments44# Build pkg and add any extra arguments
41dpkg-buildpackage -S -nc -d $@45dpkg-buildpackage -S -nc -d $@
diff --git a/check-OS-packages-on-server-mapping.sh b/cpaelzer/check-OS-packages-on-server-mapping.sh
42similarity index 100%46similarity index 100%
43rename from check-OS-packages-on-server-mapping.sh47rename from check-OS-packages-on-server-mapping.sh
44rename to cpaelzer/check-OS-packages-on-server-mapping.sh48rename to cpaelzer/check-OS-packages-on-server-mapping.sh
diff --git a/check-autopkgtest-stats.sh b/cpaelzer/check-autopkgtest-stats.sh
45similarity index 72%49similarity index 72%
46rename from check-autopkgtest-stats.sh50rename from check-autopkgtest-stats.sh
47rename to cpaelzer/check-autopkgtest-stats.sh51rename to cpaelzer/check-autopkgtest-stats.sh
index be40820..d0ae31d 100755
--- a/check-autopkgtest-stats.sh
+++ b/cpaelzer/check-autopkgtest-stats.sh
@@ -1,17 +1,18 @@
1#!/bin/bash1#!/bin/bash
2RELEASES="$(distro-info --supported | xargs)"2RELEASES="$(distro-info --supported | xargs)"
3ARCHES="amd64 i386 ppc64el s390x armhf"3ARCHES="amd64 i386 ppc64el s390x armhf arm64"
4PACKAGE="systemd"4PACKAGE="systemd"
5PATTERN=""5PATTERN=""
6COUNT=206COUNT=20
7VERBOSE=07VERBOSE=0
8NOCLEAN=0
8WDIR=""9WDIR=""
9PATTERNLOG="custom-pattern.log"10PATTERNLOG="custom-pattern.log"
1011
11while getopts "hp:P:a:r:c:v" opt; do12while getopts "hp:P:a:r:c:vC" opt; do
12 case "$opt" in13 case "$opt" in
13 h|\?)14 h|\?)
14 echo "$0 [-p <src:package>] [-r 'list of releases'] [-a 'list of architectures'] [-v] [-P 'pattern' to be summed up] [-c <count> to check (default 20)]"15 echo "$0 [-p <src:package>] [-r 'list of releases'] [-a 'list of architectures'] [-v] [-C] [-P 'pattern' to be summed up] [-c <count> to check (default 20)]"
15 exit 016 exit 0
16 ;;17 ;;
17 p) PACKAGE=$OPTARG18 p) PACKAGE=$OPTARG
@@ -24,6 +25,8 @@ while getopts "hp:P:a:r:c:v" opt; do
24 ;;25 ;;
25 c) COUNT=$OPTARG26 c) COUNT=$OPTARG
26 ;;27 ;;
28 C) NOCLEAN=1
29 ;;
27 v) VERBOSE=130 v) VERBOSE=1
28 ;;31 ;;
29 esac32 esac
@@ -32,7 +35,7 @@ done
32echo "Check last ${COUNT} test results for src:${PACKAGE} on releases '${RELEASES}' on architectures '${ARCHES}'"35echo "Check last ${COUNT} test results for src:${PACKAGE} on releases '${RELEASES}' on architectures '${ARCHES}'"
3336
34cleanup() {37cleanup() {
35 if [[ -n "${WDIR}" && -d "${WDIR}" && ${WDIR} == /tmp/* ]]; then38 if [[ ${NOCLEAN} -ne 1 && -n "${WDIR}" && -d "${WDIR}" && ${WDIR} == /tmp/* ]]; then
36 rm -Rf "${WDIR}"39 rm -Rf "${WDIR}"
37 fi40 fi
38}41}
@@ -47,22 +50,37 @@ WDIR=$(mktemp -d) || { echo "failed to make tempdir"; exit 1; }
47trap cleanup EXIT50trap cleanup EXIT
48cd ${WDIR}51cd ${WDIR}
4952
50echo "Fetch Data to ${WDIR}"53if [ ${VERBOSE} -eq 1 ]; then
54 echo "Fetch Data to ${WDIR}"
55fi
56
51for arch in ${ARCHES}; do57for arch in ${ARCHES}; do
52 debug "A: ${arch}"58 debug "A: ${arch}"
53 for release in ${RELEASES}; do59 for release in ${RELEASES}; do
54 debug " R: ${arch}-${release}"60 debug " R: ${arch}-${release}"
55 wget --quiet "http://autopkgtest.ubuntu.com/packages/${PACKAGE:0:1}/${PACKAGE}/${release}/${arch}" -O "${release}-${arch}.history"61 wget --quiet "http://autopkgtest.ubuntu.com/packages/${PACKAGE:0:1}/${PACKAGE}/${release}/${arch}" -O "${release}-${arch}.history"
62 debug " U: http://autopkgtest.ubuntu.com/packages/${PACKAGE:0:1}/${PACKAGE}/${release}/${arch}"
56 logs=$(awk -F '"' '/>log<\/a>/ {print $2}' "${release}-${arch}.history" | head -n "${COUNT}")63 logs=$(awk -F '"' '/>log<\/a>/ {print $2}' "${release}-${arch}.history" | head -n "${COUNT}")
57 i=064 i=0
58 for log in ${logs}; do65 for log in ${logs}; do
59 debug " L: ${release}-${arch}.$((++i))"66 debug " L: ${release}-${arch}.$((++i)) => ${log}"
60 wget --quiet "${log}" -O "${release}-${arch}.${i}" &67 wget --quiet "${log}" -O "${release}-${arch}.${i}" &
61 done68 done
62 done69 done
63done70done
64wait71wait
6572
73badtest() {
74 for testname in "${!FAILLOG[@]}"; do
75 FAILLOG["${testname}"]="${FAILLOG["${testname}"]}B"
76 done
77}
78
79timeouttest() {
80 for testname in "${!FAILLOG[@]}"; do
81 FAILLOG["${testname}"]="${FAILLOG["${testname}"]}T"
82 done
83}
66tracefaillog() {84tracefaillog() {
67 tlist="${1}"85 tlist="${1}"
68 symbol="${2}"86 symbol="${2}"
@@ -81,10 +99,14 @@ for release in ${RELEASES}; do
81 failures=""99 failures=""
82 unset FAILLOG100 unset FAILLOG
83 declare -A FAILLOG101 declare -A FAILLOG
84 for log in $(ls ${release}-${arch}.[0-9]*); do102 for log in $(ls -v ${release}-${arch}.[0-9]*); do
85 newfail="$(zcat "${log}" | sed -n -e '/^autopkgtest.*summary/,$p' | awk '/FAIL/ {print $1}')"103 # some aborts have what seems like results in between (recursive runs), ignore all but the last 100 lines
86 newpass="$(zcat "${log}" | sed -n -e '/^autopkgtest.*summary/,$p' | awk '/PASS/ {print $1}')"104 # furthermore some results get reported twice
87 newskip="$(zcat "${log}" | sed -n -e '/^autopkgtest.*summary/,$p' | awk '/SKIP/ {print $1}')"105 zcat "${log}" | tail -n 150 | sed -ne '/^autopkgtest.*@@.*summary$/,$ p' | sort | uniq > "${log}.results"
106 newfail="$(awk '/\sFAIL\s/ {print $1}' ${log}.results)"
107 newpass="$(awk '/\sPASS$/ {print $1}' ${log}.results)"
108 newskip="$(awk '/\sSKIP\s/ {print $1}' ${log}.results)"
109 newflak="$(awk '/\sFLAKY\s/ {print $1}' ${log}.results)"
88 if [ -n "${PATTERN}" ]; then110 if [ -n "${PATTERN}" ]; then
89 zcat "${log}" | grep -e "${PATTERN}" >> "${PATTERNLOG}"111 zcat "${log}" | grep -e "${PATTERN}" >> "${PATTERNLOG}"
90 fi112 fi
@@ -94,14 +116,32 @@ for release in ${RELEASES}; do
94 tracefaillog "${newfail}" "F"116 tracefaillog "${newfail}" "F"
95 tracefaillog "${newpass}" "."117 tracefaillog "${newpass}" "."
96 tracefaillog "${newskip}" "S"118 tracefaillog "${newskip}" "S"
119 tracefaillog "${newflak}" "f"
120 if [[ -z "${newfail}${newpass}${newskip}${newflak}" ]]; then
121 if zcat "${log}" | grep -q '<VirtSubproc>: failure: Timed out on waiting for ssh connection'; then
122 timeouttest
123 else
124 badtest
125 fi
126 fi
97 done127 done
98 printf " ${arch}\n"128 printf " ${arch}\n"
99 if [ -n "${failures}" ]; then129 if [ -n "${failures}" ]; then
100 countedfails=$(echo "${failures}" | sed -r '/^\s*$/d' | sort | uniq -c | awk --assign count=${COUNT} '{ percent=($1/count)*100; printf("%-40s (%6.2lf%%)\n", $0, percent)}')130 for testname in "${!FAILLOG[@]}"; do
101 while read -r line; do131 countfail=$(echo ${FAILLOG["${testname}"]} | tr -cd 'F' | wc -c)
102 testname=$(echo ${line} | awk '{print $2}' | tr -d .)132 countskip=$(echo ${FAILLOG["${testname}"]} | tr -cd 'S' | wc -c)
103 printf "%50s %s\n" "${line}" ${FAILLOG["${testname}"]}133 countpass=$(echo ${FAILLOG["${testname}"]} | tr -cd '.' | wc -c)
104 done <<< "${countedfails}"134 countflak=$(echo ${FAILLOG["${testname}"]} | tr -cd 'f' | wc -c)
135 countbad=$(echo ${FAILLOG["${testname}"]} | tr -cd 'B' | wc -c)
136 if [ $COUNT -ne $((countpass+countbad)) ]; then
137 printf " %-30s (F %2u%% f %2u%% S %2u%% B %2u%% => P %2u%%/) %s\n" \
138 "${testname}" \
139 "$((countfail*100/COUNT))" "$((countflak*100/COUNT))" \
140 "$((countskip*100/COUNT))" \
141 "$((countbad*100/COUNT))" "$((countpass*100/COUNT))" \
142 ${FAILLOG["${testname}"]}
143 fi
144 done
105 else145 else
106 echo " no failures"146 echo " no failures"
107 fi147 fi
diff --git a/check-component-mismatches-v2.sh b/cpaelzer/check-component-mismatches-v2.sh
108similarity index 100%148similarity index 100%
109rename from check-component-mismatches-v2.sh149rename from check-component-mismatches-v2.sh
110rename to cpaelzer/check-component-mismatches-v2.sh150rename to cpaelzer/check-component-mismatches-v2.sh
diff --git a/check-component-mismatches.sh b/cpaelzer/check-component-mismatches.sh
111similarity index 100%151similarity index 100%
112rename from check-component-mismatches.sh152rename from check-component-mismatches.sh
113rename to cpaelzer/check-component-mismatches.sh153rename to cpaelzer/check-component-mismatches.sh
diff --git a/dput b/cpaelzer/dput
114similarity index 84%154similarity index 84%
115rename from dput155rename from dput
116rename to cpaelzer/dput156rename to cpaelzer/dput
index 54aedcf..4523488 100755
--- a/dput
+++ b/cpaelzer/dput
@@ -5,6 +5,16 @@ CMD="/usr/bin/dput"
5# Checker for silly mistakes (I seem to have others than other people)5# Checker for silly mistakes (I seem to have others than other people)
6# So I need to check mine on my own6# So I need to check mine on my own
77
8if [ "$#" -ne 2 ]; then
9 echo "Usage with two args: ubuntu/ftp-master .changes-file, got $@"
10 exit 1
11fi
12
13if grep "LP:.*9999" $2; then
14 echo "Likely left a placeholder in the changelog"
15 err=1
16fi
17
8if [ -z "$2" ]; then18if [ -z "$2" ]; then
9 echo "Only one Arg is unsafe (and would break later)"19 echo "Only one Arg is unsafe (and would break later)"
10 err=120 err=1
diff --git a/cpaelzer/excuses-top-blocker.sh b/cpaelzer/excuses-top-blocker.sh
11new file mode 10075521new file mode 100755
index 0000000..5941819
--- /dev/null
+++ b/cpaelzer/excuses-top-blocker.sh
@@ -0,0 +1,4 @@
1f=$(mktemp)
2wget https://people.canonical.com/~ubuntu-archive/proposed-migration/update_excuses.html -O $f
3awk '/not considered/ {gsub(".*a href=\"#",""); gsub("\">.*",""); print $0}' $f | sort | uniq -c | sort -n
4rm $f
diff --git a/get-packages-subscribed.py b/cpaelzer/get-packages-subscribed.py
0similarity index 98%5similarity index 98%
1rename from get-packages-subscribed.py6rename from get-packages-subscribed.py
2rename to cpaelzer/get-packages-subscribed.py7rename to cpaelzer/get-packages-subscribed.py
index ae14b7f..9ac37bc 100755
--- a/get-packages-subscribed.py
+++ b/cpaelzer/get-packages-subscribed.py
@@ -1,4 +1,4 @@
1#! /usr/bin/python1#! /usr/bin/python3
2"""2"""
3Derived from ubuntu-archive-tools package-subscribers to detemine3Derived from ubuntu-archive-tools package-subscribers to detemine
4the list of subscribers for a Team4the list of subscribers for a Team
diff --git a/git-to-dquilt.sh b/cpaelzer/git-to-dquilt.sh
5similarity index 62%5similarity index 62%
6rename from git-to-dquilt.sh6rename from git-to-dquilt.sh
7rename to cpaelzer/git-to-dquilt.sh7rename to cpaelzer/git-to-dquilt.sh
index 0296191..3855bb3 100755
--- a/git-to-dquilt.sh
+++ b/cpaelzer/git-to-dquilt.sh
@@ -5,6 +5,12 @@ set -uxe
5# git-to-dquilt.sh <bugnumber>|none git-base-url commit[, ...]5# git-to-dquilt.sh <bugnumber>|none git-base-url commit[, ...]
6# will apply commits in order and convert to d/p/ entries with dep3 headers6# will apply commits in order and convert to d/p/ entries with dep3 headers
77
8if [ -e "${HOME}/.quiltrc-dpkg" ]; then
9 QUILTRC="${HOME}/.quiltrc-dpkg"
10else
11 QUILTRC="${HOME}/.quiltrc"
12fi
13
8insert_patch_template() {14insert_patch_template() {
9 local patch=${1}15 local patch=${1}
10 local commiturl=${2}16 local commiturl=${2}
@@ -15,7 +21,11 @@ insert_patch_template() {
15 then21 then
16 echo ""22 echo ""
17 echo "Origin: upstream, ${commiturl}"23 echo "Origin: upstream, ${commiturl}"
18 if [[ ${BUG} != "none" ]]; then24 if [[ ${BUG} == SF* ]]; then
25 echo "Bug-SF: ${BUG}"
26 elif [[ ${BUG} == CVE* ]]; then
27 echo "CVE: https://ubuntu.com/security/${BUG}"
28 elif [[ ${BUG} != "none" ]]; then
19 echo "Bug-Ubuntu: https://bugs.launchpad.net/bugs/${BUG}"29 echo "Bug-Ubuntu: https://bugs.launchpad.net/bugs/${BUG}"
20 fi30 fi
21 echo "Last-Update: $(date +%F)"31 echo "Last-Update: $(date +%F)"
@@ -48,17 +58,28 @@ get_commit_id() {
48}58}
4959
50apply_commit() {60apply_commit() {
51 commit=${1}61 local commit=${1}
62 local suffix=""
63 local patch=""
64 local prefix="${PREFIX}"
5265
53 patch=$(git format-patch -1 "${commit}")66 patch=$(git format-patch -1 "${commit}")
54 if [ $(pwd) == *libvirt* || $(pwd) == *qemu* ]; then67 # auto prefix detection for some projects if not set externally
55 prefix="ubuntu"68 if [ -z "${PREFIX}" ]; then
56 else69 if [[ $(pwd) == *libvirt* || $(pwd) == *qemu* ]]; then
57 prefix=""70 prefix="ubuntu/"
71 else
72 prefix=""
73 fi
58 fi74 fi
75 [[ -n "${prefix}" && "${prefix}" != */ ]] && prefix="${prefix}/"
5976
60 if [[ ${BUG} == "none" ]]; then77 if [[ ${BUG} == "none" ]]; then
61 patch=$(rename -v "s/000[0-9]-//" "${patch}" | awk '{print $NF}')78 patch=$(rename -v "s/000[0-9]-//" "${patch}" | awk '{print $NF}')
79 elif [[ ${BUG} == SF* ]]; then
80 patch=$(rename -v "s/000[0-9]/sf-${BUG}/" "${patch}" | awk '{print $NF}')
81 elif [[ ${BUG} == CVE* ]]; then
82 patch=$(rename -v "s/000[0-9]/${BUG}/" "${patch}" | awk '{print $NF}')
62 else83 else
63 patch=$(rename -v "s/000[0-9]/lp-${BUG}/" "${patch}" | awk '{print $NF}')84 patch=$(rename -v "s/000[0-9]/lp-${BUG}/" "${patch}" | awk '{print $NF}')
64 fi85 fi
@@ -66,14 +87,52 @@ apply_commit() {
6687
67 insert_patch_template "${patch}" "${URL}${commit}"88 insert_patch_template "${patch}" "${URL}${commit}"
6889
69 quilt --quiltrc=/home/paelzer/.quiltrc-dpkg import "${patch}" -P "${prefix}${patch}"90 if [ -e "debian/patches/${prefix}${patch}" ]; then
70 quilt --quiltrc=/home/paelzer/.quiltrc-dpkg push --fuzz=091 suffix="1"
92 while [ -e "debian/patches/${prefix}${patch}-${suffix}" ]; do
93 suffix=$((suffix + 1))
94 done
95 suffix="-${suffix}"
96 fi
97
98 quilt --quiltrc="${QUILTRC}" import "${patch}" -P "${prefix}${patch}${suffix}"
99 quilt --quiltrc="${QUILTRC}" push --fuzz=0
71 rm "${patch}"100 rm "${patch}"
72}101}
73102
74if [ $# -lt 3 ]; then103if [ $# -lt 3 ]; then
75 echo "usage $0 <bugnumber>|none git-base-url commit[, ...]"104 echo "usage $0 [-b <bugnumber>] -u git-base-url [-p prefix] commit[, ...]"
76 echo "remember ; in URls will end the command string"105 echo "-b bug number for dep-3 headers"
106 echo "-p prefix for the patch name"
107 echo "-u URL to reach the commit"
108 echo "remember ; in URls will often include an &, best use '' around it"
109 exit 1
110fi
111
112BUG="none"
113URL=""
114PREFIX=""
115
116while getopts ":b:u:p:" opt; do
117 case ${opt} in
118 b )
119 BUG=${OPTARG}
120 ;;
121 u )
122 URL=${OPTARG}
123 ;;
124 p )
125 PREFIX=${OPTARG}
126 ;;
127 \? ) echo "Usage: $0 [-b <bugno>] -u <base-git-URL> [-p <prefix>] git-hash..."
128 ;;
129 esac
130done
131shift $((OPTIND-1))
132
133
134if [ -z "${URL}" ]; then
135 echo "You must set an URL"
77 exit 1136 exit 1
78fi137fi
79138
@@ -85,16 +144,13 @@ fi
85144
86# might be the first, can't push in that case145# might be the first, can't push in that case
87if [ -s debian/patches/series ]; then146if [ -s debian/patches/series ]; then
88 quilt --quiltrc=/home/paelzer/.quiltrc-dpkg push -a --fuzz=0147 quilt --quiltrc="${QUILTRC}" push -a --fuzz=0
89fi148fi
90149
91BUG=${1:-none}150for commitid in "${@}"; do
92URL=${2:-""}
93
94for commitid in "${@:3}"; do
95 apply_commit "${commitid}"151 apply_commit "${commitid}"
96done152done
97153
98quilt --quiltrc=/home/paelzer/.quiltrc-dpkg pop -a154# report final status
99155quilt --quiltrc="${QUILTRC}" pop -a
100git status156git status
diff --git a/locallibvirt.sh b/cpaelzer/locallibvirt.sh
101similarity index 100%157similarity index 100%
102rename from locallibvirt.sh158rename from locallibvirt.sh
103rename to cpaelzer/locallibvirt.sh159rename to cpaelzer/locallibvirt.sh
diff --git a/localrepo.sh b/cpaelzer/localrepo.sh
104similarity index 100%160similarity index 100%
105rename from localrepo.sh161rename from localrepo.sh
106rename to cpaelzer/localrepo.sh162rename to cpaelzer/localrepo.sh
diff --git a/lp-affects-devel b/cpaelzer/lp-affects-devel
107similarity index 100%163similarity index 100%
108rename from lp-affects-devel164rename from lp-affects-devel
109rename to cpaelzer/lp-affects-devel165rename to cpaelzer/lp-affects-devel
diff --git a/cpaelzer/lp-test-isrunning b/cpaelzer/lp-test-isrunning
110new file mode 100755166new file mode 100755
index 0000000..fc36d6c
--- /dev/null
+++ b/cpaelzer/lp-test-isrunning
@@ -0,0 +1,61 @@
1#!/usr/bin/python3
2# Credit goes to APW who shared this with me - thanks!
3
4import datetime
5import os
6import sys
7
8import urllib.request
9import json
10
11#urllib.request.urlcleanup()
12request = urllib.request.Request('http://autopkgtest.ubuntu.com/static/running.json')
13request.add_header('Cache-Control', 'max-age=0')
14with urllib.request.urlopen(request) as response:
15 data = response.read()
16 jobs = json.loads(data.decode('utf-8'))
17
18
19running = []
20for pkg in jobs:
21 for handle in jobs[pkg]:
22 for series in jobs[pkg][handle]:
23 for arch in jobs[pkg][handle][series]:
24 jobinfo = jobs[pkg][handle][series][arch]
25 triggers = ','.join(jobinfo[0].get('triggers', '-'))
26 ppas = ','.join(jobinfo[0].get('ppas', '-'))
27 time = jobinfo[1]
28 env = jobinfo[0].get('env', '-')
29 time = str(datetime.timedelta(seconds=jobinfo[1]))
30 try:
31 running.append((jobinfo[1], "R {6:6} {0:30} {5:10} {1:8} {2:8} {3:31} {4} {7}".format(pkg, series, arch, ppas, triggers, '-', time, env)))
32 except BrokenPipeError:
33 sys.exit(1)
34
35for (time, row) in sorted(running, reverse=True):
36 print(row)
37
38request = urllib.request.Request('http://autopkgtest.ubuntu.com/queues.json')
39request.add_header('Cache-Control', 'max-age=0')
40with urllib.request.urlopen(request) as response:
41 data = response.read()
42 queues = json.loads(data.decode('utf-8'))
43
44 for origin in queues:
45 for series in queues[origin]:
46 for arch in queues[origin][series]:
47 n = 0
48 for key in queues[origin][series][arch]:
49 (pkg, json_data) = key.split(maxsplit=1)
50 jobinfo = json.loads(json_data)
51
52 triggers = ','.join(jobinfo.get('triggers', '-'))
53 ppas = ','.join(jobinfo.get('ppas', '-'))
54
55 n = n + 1
56 try:
57 print("Q{5:04d} {7:>6} {0:30} {6:10} {1:8} {2:8} {3:31} {4}".format(pkg, series, arch, ppas, triggers, n, origin, '-:--'))
58 except BrokenPipeError:
59 sys.exit(1)
60
61 # /Running for:/ { printf("%-30s %-8s %-8s%-31s%s\n", pkg, release, arch, ppas, triggers); next }
diff --git a/cpaelzer/lp-test-ppa b/cpaelzer/lp-test-ppa
0new file mode 10075562new file mode 100755
index 0000000..b467f2d
--- /dev/null
+++ b/cpaelzer/lp-test-ppa
@@ -0,0 +1,395 @@
1#!/usr/bin/env python3
2# Thanks to APW for the part around getRunning
3
4import argparse
5import datetime
6import gzip
7import json
8import os
9import re
10import urllib.request
11import urllib.parse
12import sys
13import time
14
15from launchpadlib.credentials import UnencryptedFileCredentialStore
16from launchpadlib.launchpad import Launchpad
17
18
19def getRunning():
20 request = urllib.request.Request('http://autopkgtest.ubuntu.com/static/running.json')
21 request.add_header('Cache-Control', 'max-age=0')
22 with urllib.request.urlopen(request) as response:
23 data = response.read()
24 jobs = json.loads(data.decode('utf-8'))
25
26 running = []
27 for pkg in jobs:
28 for handle in jobs[pkg]:
29 for series in jobs[pkg][handle]:
30 for arch in jobs[pkg][handle][series]:
31 jobinfo = jobs[pkg][handle][series][arch]
32 triggers = ','.join(jobinfo[0].get('triggers', '-'))
33 ppas = ','.join(jobinfo[0].get('ppas', '-'))
34 time = str(datetime.timedelta(seconds=jobinfo[1]))
35 running.append({"time": time,
36 "pkg": pkg,
37 "release": series,
38 "arch": arch,
39 "ppa": ppas,
40 "trigger": triggers})
41
42 return sorted(running, key=lambda k: k['time'])
43
44
45def getWaiting():
46 request = urllib.request.Request('http://autopkgtest.ubuntu.com/queues.json')
47 request.add_header('Cache-Control', 'max-age=0')
48 with urllib.request.urlopen(request) as response:
49 data = response.read()
50 queues = json.loads(data.decode('utf-8'))
51
52 waiting = []
53 for origin in queues:
54 for series in queues[origin]:
55 for arch in queues[origin][series]:
56 n = 0
57 for key in queues[origin][series][arch]:
58 (pkg, json_data) = key.split(maxsplit=1)
59 jobinfo = json.loads(json_data)
60
61 triggers = ','.join(jobinfo.get('triggers', '-'))
62 ppas = ','.join(jobinfo.get('ppas', '-'))
63
64 n = n + 1
65 waiting.append({"number": n,
66 "pkg": pkg,
67 "release": series,
68 "arch": arch,
69 "ppa": ppas,
70 "trigger": triggers})
71
72 return waiting
73
74
75def getLink(url, text, end='\n'):
76 return f"\u001b]8;;{url}\u001b\\{text}\u001b]8;;\u001b\\"
77
78
79def showTriggers(trigger_name, trigger_version, release,
80 ppa_user, ppa_name, test_name):
81 urlfmt = ("https://autopkgtest.ubuntu.com/request.cgi?release=%s" +
82 "&arch=%s&package=%s&ppa=%s/%s&trigger=%s/%s")
83 req_url = urlfmt % (release, "%s", test_name,
84 ppa_user, ppa_name, trigger_name,
85 "%s")
86 enc_version = urllib.parse.quote_plus(trigger_version)
87
88 # Triggers for all Architectures
89 for arch in ARCHES:
90 # list src@arch header + triggers
91 print(" %20s @ %-7s for %s/%s" % (test_name, arch, trigger_name,
92 trigger_version), end='')
93 arch_req_url = req_url % (arch, enc_version)
94 print(" %s" % getLink(arch_req_url, "Trigger @%s ♻️ " % arch),
95 end='')
96 print(" %s" % getLink("%s&all-proposed=1" % arch_req_url,
97 "Trigger all proposed @%s ♻️ 💍" % arch))
98
99
100def showActive(release, ppa_user, ppa_name):
101 target = "%s/%s" % (ppa_user, ppa_name)
102 rformat = " %-8s %-40s %-8s %-8s %-40s %s"
103
104 print("Running:")
105 running = getRunning()
106 running = [e for e in running if e['release'] == release]
107 running = [e for e in running if e['ppa'] == target]
108 if running:
109 print(rformat % ("time", "pkg", "release", "arch", "ppa", "trigger"))
110 for e in running:
111 print(rformat % (e['time'], e['pkg'], e['release'], e['arch'],
112 e['ppa'], e['trigger']))
113
114 print("Waiting:")
115 waiting = getWaiting()
116 waiting = [e for e in waiting if e['release'] == release]
117 waiting = [e for e in waiting if e['ppa'] == target]
118 if waiting:
119 print(rformat % ("Q-num", "pkg", "release", "arch", "ppa", "trigger"))
120 for e in waiting:
121 print(rformat % (e['number'], e['pkg'], e['release'], e['arch'],
122 e['ppa'], e['trigger']))
123
124
125def showResults(release, ppa_user, ppa_name, published):
126 resfmt = ("https://autopkgtest.ubuntu.com/results/" +
127 "autopkgtest-%s-%s-%s/?format=plain")
128 result_list = resfmt % (release, ppa_user, ppa_name)
129
130 base_resultfmt = ("https://autopkgtest.ubuntu.com/results/" +
131 "autopkgtest-%s-%s-%s/")
132 base_result = base_resultfmt % (release, ppa_user, ppa_name)
133
134 print("Results from %s:" % result_list)
135 results_web = []
136 try:
137 with urllib.request.urlopen(result_list) as response:
138 html = response.read()
139 results_web = html.split(b'\n')
140 except urllib.error.HTTPError:
141 print(" No results published yet")
142
143 # gather results
144 result_data = {}
145 for result in results_web:
146 if result == b'' or not result.endswith(b"log.gz"):
147 continue
148 res_url = base_result + result.decode("utf-8")
149 res_elements = res_url.split("/")
150 res_time = time.strptime(res_elements[9][:-7], "%Y%m%d_%H%M%S")
151 res_arch = res_elements[6]
152 res_source = res_elements[8]
153 if (res_source, res_arch) not in result_data:
154 result_data[(res_source, res_arch)] = []
155 result_data[(res_source, res_arch)].append((res_time, res_url))
156
157 # report
158 for source_arch in result_data:
159 # skip non selected architectures
160 if source_arch[1] not in ARCHES:
161 continue
162 # if packagestoshow is set, skip all others
163 if args.packagestoshow:
164 if source_arch[0] not in args.packagestoshow[0]:
165 continue
166
167 # header
168 print(" %s @ %s:" % (source_arch[0], source_arch[1]))
169 if args.showonlylast:
170 result_data[source_arch] = [result_data[source_arch][-1]]
171 for result in result_data[source_arch]:
172 showResult(result, published)
173
174
175def showResultID(result):
176 print(" %10s" % time.strftime("%d.%m.%y %H:%M:%S", result[0]), end='')
177 print(" %s" % getLink(result[1], "Log 🗒️"), end='')
178
179
180def showResult(result, published):
181 with urllib.request.urlopen(result[1]) as result_response:
182 result_gzip = result_response.read()
183 try:
184 result_log = gzip.decompress(result_gzip).decode("utf-8")
185 except UnicodeDecodeError:
186 showResultID(result)
187 print("\n Broken Test Log ⚪")
188 return
189
190 trigger = []
191 res_trigger = re.search("--env=ADT_TEST_TRIGGERS=.* -- ", result_log)
192 if not res_trigger:
193 report_txt = " Trigger not listed in log ⚪"
194 else:
195 res_trigger = res_trigger.group(0)
196 res_trigger = res_trigger[len("--env=ADT_TEST_TRIGGERS="):]
197 res_trigger = res_trigger[:res_trigger.find(" -- ")]
198 res_trigger = res_trigger.strip("'")
199 trigger = res_trigger.split()
200 if args.trigger and args.trigger not in trigger:
201 return
202 report_txt = (" Triggers: %s" % trigger)
203
204 PublishedInTrigger = False
205 if args.selecttrigger == "published":
206 for pubpkg in published:
207 published_trigger = "%s/%s" % (pubpkg['pkg'], pubpkg['vers'])
208 if published_trigger in trigger:
209 PublishedInTrigger = True
210 elif args.selecttrigger is not None:
211 if args.selecttrigger in trigger:
212 PublishedInTrigger = True
213 else:
214 PublishedInTrigger = True
215 if not PublishedInTrigger:
216 return
217
218 showResultID(result)
219 result_split = result_log.split("@@@@@@@@@@@@@@@@@@@@ summary", 1)
220 if len(result_split) < 2:
221 print("\t⚪ %s\n" % report_txt, end='')
222 print(" %-50s" % ("No valid results"))
223 return
224 result_sum = result_split[1]
225 result_lines = re.findall("(.*PASS|.*SKIP|.*FAIL|.*BAD)", result_sum)
226
227 for line in result_lines:
228 report_result = True
229 overall_status = "✅"
230 status = "⚪"
231 if args.subtest and not line.startswith(args.subtest):
232 report_result = False
233 if line.endswith("SKIP"):
234 status = "🟧"
235 if not args.showskip:
236 report_result = False
237 if line.endswith("PASS"):
238 status = "✅"
239 if not args.showpass:
240 report_result = False
241 if line.endswith("FAIL"):
242 status = "🟥"
243 overall_status = "🟥"
244 if report_result:
245 report_txt += ("\n %-50s %s " % (line, status))
246
247 print("\t%s %s" % (overall_status, report_txt))
248
249
250# Argument parsing
251parser = argparse.ArgumentParser()
252parser.add_argument('ppa',
253 default=None,
254 help='name of the ppa to test in ppa:user/name format')
255parser.add_argument('-r', '--release',
256 default=None,
257 action='append',
258 nargs='+',
259 dest="releases",
260 help='release(s) to test')
261parser.add_argument('-a', '--arch',
262 default=None,
263 action='append',
264 nargs='+',
265 dest="arches",
266 help='Architectures to process (default = all)')
267parser.add_argument('-t', '--testpackage',
268 default=None,
269 action='append',
270 nargs='+',
271 dest="testpackages",
272 help='Additional packages to list trigger links for')
273parser.add_argument('-p', '--package',
274 default=None,
275 action='append',
276 nargs='+',
277 dest="packagestoshow",
278 help='Only interested in the results of <package>')
279parser.add_argument('-s', '--subtest',
280 default=None,
281 dest="subtest",
282 help='Only report (sub)tests with the given name')
283parser.add_argument('--trigger',
284 default=None,
285 dest="trigger",
286 help='Only report results that contain this trigger')
287parser.add_argument('--showpass',
288 default=False,
289 dest="showpass",
290 action="store_true",
291 help='Show passing subtests'
292 ' (overall status is always shown)')
293parser.add_argument('--showskip',
294 default=False,
295 dest="showskip",
296 action="store_true",
297 help='Show skipped subtests'
298 ' (overall status is always shown)')
299parser.add_argument('-l', '--last',
300 default=False,
301 dest="showonlylast",
302 action="store_true",
303 help='Show only the last test result per test')
304parser.add_argument('-f', '--select-trigger',
305 default=None,
306 dest="selecttrigger",
307 help='Show only results which trigger contains this string'
308 '; Special value "published" only shows if any of the'
309 ' currently published source/versions are in the'
310 ' triggers.')
311args = parser.parse_args()
312
313ppa_user = args.ppa.split(":")[1].split("/")[0]
314ppa_name = args.ppa.split(":")[1].split("/")[1]
315
316# log in
317cred_location = os.path.expanduser('~/.lp_creds')
318credential_store = UnencryptedFileCredentialStore(cred_location)
319lp = Launchpad.login_with('affectrelease', 'production', version='devel')
320
321# project/person owning the PPA
322person = lp.people[ppa_user]
323
324if args.arches:
325 ARCHES = args.arches[0]
326else:
327 ARCHES = ["amd64", "s390x", "ppc64el", "arm64", "armhf", "riscv64"]
328
329# get ppa
330ppa = person.getPPAByName(name=ppa_name)
331if ppa is not None:
332 print("Tests for %s" % getLink(ppa.web_link, "PPA %s" % ppa_name))
333 for release in args.releases[0]:
334 print("---- ---- ---- ----")
335 print("Release: %s" % release)
336 series = "https://api.launchpad.net/1.0/ubuntu/%s" % release
337 ppa_sources = ppa.getPublishedSources(distro_series=series)
338 if not ppa_sources:
339 print("Warning: no sources present for %s" % release)
340 else:
341 print("Sources:")
342 for source in ppa_sources:
343 print(" SRC: %s @ %s - %s" % (source.source_package_name,
344 source.source_package_version,
345 source.status))
346 print("Triggers on published Sources:")
347 published = []
348 for source in ppa_sources:
349 if source.status != "Published":
350 continue
351
352 showTriggers(source.source_package_name,
353 source.source_package_version,
354 release, ppa_user, ppa_name,
355 source.source_package_name)
356 published.append({"pkg": source.source_package_name,
357 "vers": source.source_package_version})
358
359 if args.testpackages:
360 for test_name in args.testpackages[0]:
361 print("Requested tests triggered vs published sources:")
362 showTriggers(source.source_package_name,
363 source.source_package_version, release,
364 ppa_user, ppa_name, test_name)
365
366 showResults(release, ppa_user, ppa_name, published)
367 showActive(release, ppa_user, ppa_name)
368else:
369 print("Error: PPA %s not found" % ppa_name)
370 sys.exit(1)
371
372
373# FYI - Bryce has a tool for the earlier steps that does:
374# - open and configure a PPA
375# - upload changes and build
376# - wait for the buidl to be complete
377# it is yet unpublished, but I'll remove my former todo's for this stages
378
379# TODO
380# - get release from PPA (if not set on cmdline)
381# - cmdline: restart-all-failed (and only those)
382# - cmdline: trigger-tests-for-all-not-yet-sucess-tested-sources-in-ppa
383# - if tests results are present, then also show triggers for them (even if we
384# have no source of it)
385# - do not show header and log is "skipshow" is set and all results are good
386# - split "showResult" into "parseResult" filling an object/struct to then
387# have much more clear logic control flow on that and eventually print it
388# in "showResult"
389# - a ppa can have multiple revisions of a package. Usually one is only
390# interested in the tests of the last one, provide an option to filter
391# out the others
392# - let all show* functions support filtering per selected Architecture
393# We don't open anything automatically yet, if we want (e.g. retry all failed)
394# import webbrowser
395# webbrowser.open(url[, new=0[, autoraise=True]])
diff --git a/packageset-subscription-mismatches.sh b/cpaelzer/packageset-subscription-mismatches.sh
0similarity index 100%396similarity index 100%
1rename from packageset-subscription-mismatches.sh397rename from packageset-subscription-mismatches.sh
2rename to cpaelzer/packageset-subscription-mismatches.sh398rename to cpaelzer/packageset-subscription-mismatches.sh
diff --git a/pull-uca-source.py b/cpaelzer/pull-uca-source.py
3similarity index 100%399similarity index 100%
4rename from pull-uca-source.py400rename from pull-uca-source.py
5rename to cpaelzer/pull-uca-source.py401rename to cpaelzer/pull-uca-source.py
diff --git a/cpaelzer/rma b/cpaelzer/rma
6new file mode 100755402new file mode 100755
index 0000000..4ccbc3b
--- /dev/null
+++ b/cpaelzer/rma
@@ -0,0 +1,6 @@
1#!/bin/bash
2for p in $@; do
3 rmadison -u debian $p &
4 rmadison $p &
5done
6wait
diff --git a/sbuildchroot b/cpaelzer/sbuildchroot
0similarity index 100%7similarity index 100%
1rename from sbuildchroot8rename from sbuildchroot
2rename to cpaelzer/sbuildchroot9rename to cpaelzer/sbuildchroot
diff --git a/testify.sh b/cpaelzer/testify.sh
3similarity index 100%10similarity index 100%
4rename from testify.sh11rename from testify.sh
5rename to cpaelzer/testify.sh12rename to cpaelzer/testify.sh
diff --git a/cpaelzer/update-git-ubuntu-urls.sh b/cpaelzer/update-git-ubuntu-urls.sh
6new file mode 10075513new file mode 100755
index 0000000..f121417
--- /dev/null
+++ b/cpaelzer/update-git-ubuntu-urls.sh
@@ -0,0 +1,27 @@
1#!/bin/bash
2checked=0
3updated=0
4found=0
5for repo in $(find ~/ -name .git -type d 2>/dev/null); do
6 ((checked++))
7 path="$(dirname $repo)"
8 cd "$path"
9 if git remote -v | grep '^pkg\s' | grep -q usd-import-team; then
10 echo "Repo $repo uses the old style, updating"
11 ((found++))
12 pkg1=$(git show pkg/ubuntu/devel:debian/control 2>/dev/null | awk '/^Source:/{print $2}')
13 if [ -z "$pkg1" ]; then
14 # local import
15 pkg1=$(git show importer/ubuntu/devel:debian/control 2>/dev/null | awk '/^Source:/{print $2}')
16 fi
17 pkg2=$(git remote -v | grep '^pkg\s' | grep usd-import-team | awk --field-separator '/' '{gsub(" .*$","",$7); print $7}' | uniq)
18 if [ "$pkg1" != "$pkg2" ]; then
19 echo "Fail: Source $pkg1 and repo $pkg2 mismatch, unsure what to do"
20 continue
21 fi
22 ((updated++))
23 git remote set-url pkg https://git.launchpad.net/ubuntu/+source/$pkg1
24 git remote set-url pkg --push ssh://paelzer@git.launchpad.net/ubuntu/+source/$pkg1
25 fi
26done
27echo "Updated $updated of $found repositories (checked: $checked)"
diff --git a/cpaelzer/versioned-rdepends.sh b/cpaelzer/versioned-rdepends.sh
0new file mode 10064428new file mode 100644
index 0000000..f306c7a
--- /dev/null
+++ b/cpaelzer/versioned-rdepends.sh
@@ -0,0 +1,6 @@
1#!/bin/bash
2# example only
3# TODO
4# replace perl with variable
5# run it in an traget environment e.g. via LXD
6for p in $(apt-cache rdepends perl | tr -d '|'); do apt-cache show $p | awk -v p="$p" '/^(Depends|Recommends):.* perl \(/ {gsub("^.* perl "," perl "); gsub(", .*",""); gsub("\\| .*",""); printf("%s %s\n",$0,p)}'; done 2>/dev/null | sort -n
diff --git a/hcdist b/hcdist
0deleted file mode 1007557deleted file mode 100755
index 015e51a..0000000
--- a/hcdist
+++ /dev/null
@@ -1,9 +0,0 @@
1#!/bin/sh
2set -e
3
4b=$1
5a=$2
6
7shift 2
8
9exec chdist "$a" "$b" "$@"
diff --git a/packages-per-repo.sh b/packages-per-repo.sh
10deleted file mode 1007550deleted file mode 100755
index 1343956..0000000
--- a/packages-per-repo.sh
+++ /dev/null
@@ -1,7 +0,0 @@
1#!/bin/bash
2tmp=$(mktemp)
3# or from dpkg --get-selections
4packages=$(dpkg -l | awk '/^ii/ {gsub(":.*",""); print $2}' | xargs)
5apt-cache policy ${packages} | awk '/^[a-zA-Z1-9-]*:/ {gsub(":",""); pkg=$1} /archive/ {printf("%s\t%s\n", $3, pkg)}' | sort | uniq > ${tmp}
6cat ${tmp}
7cut -f 1 ${tmp} | uniq -c
diff --git a/rbasak/adopt.py b/rbasak/adopt.py
8new file mode 1006440new file mode 100644
index 0000000..a9078cb
--- /dev/null
+++ b/rbasak/adopt.py
@@ -0,0 +1,80 @@
1# adopt.py
2# Author: Robie Basak
3
4# When working in a git repository that contains both maintainer branches and a
5# git-ubuntu branch, this script allows you to "adopt" a non-VCS Ubuntu upload
6# into your maintainer branch.
7#
8# Usage: adopt.py [-p <commit-ish>]... <commit-ish>
9#
10# This will add a new commit to your current branch whose tree is identical to
11# the given git-ubuntu synthesized commit. The author metadata will be taken
12# from the git-ubuntu commit, like a cherry-pick. Changelog entries will be
13# added to the commit message automatically taken from the git-ubuntu changelog
14# notes.
15#
16# Note that this isn't a cherry-pick: the tree in the commit supplied is taken
17# arbitrarily as-is.
18#
19# Example:
20#
21# An Ubuntu developer has uploaded a new upstream version to Ubuntu. You
22# maintain the packaging using the gbp workflow, but your VCS hasn't been
23# updated. You already have the "pkg" remote pointing to the git-ubuntu view of
24# your package. Now you can do:
25#
26# $ git fetch pkg
27# $ git checkout master
28# $ gbp import-orig --no-merge ../<new orig tarball>
29# $ adopt.py -p upstream pkg/ubuntu/devel
30#
31# Now your master branch is updated correctly and future merges of the upstream
32# branch will continue to work.
33#
34# Rough edges:
35#
36# The working tree is not updated, so you probably want "git reset --hard"
37# after running this command. But since this throws things away, I didn't want
38# to have the script do this automatically.
39#
40# Undescriptive error handling - just assertions and stacktraces.
41
42import argparse
43
44import debian.changelog
45import pygit2
46
47parser = argparse.ArgumentParser()
48parser.add_argument('-p', '--parent', action='append')
49parser.add_argument('commit')
50args = parser.parse_args()
51
52repo = pygit2.Repository(pygit2.discover_repository('.'))
53source_commit, _ = repo.resolve_refish(args.commit)
54assert len(source_commit.parents) == 1
55
56parents = [repo.head.peel(pygit2.Commit).id]
57if args.parent:
58 parents.extend(repo.resolve_refish(parent)[0].id for parent in args.parent)
59
60# Ideally this would use gitubuntu.git_repository.follow_symlinks_to_blob() to
61# cover various edge cases but this will do for now.
62changelog = debian.changelog.Changelog(
63 source_commit.peel(pygit2.Tree)['debian']['changelog'].data,
64)
65
66note = repo.lookup_note(str(source_commit.id), 'refs/notes/changelog')
67
68message = f'''Import {changelog.version!s} from Ubuntu
69
70New changelog entries:
71{note.message}'''
72
73repo.create_commit(
74 repo.head.name,
75 source_commit.author,
76 pygit2.Signature(repo.config['user.name'], repo.config['user.email']),
77 message,
78 source_commit.tree.id,
79 parents,
80)
diff --git a/rbasak/chdist-if-migrated b/rbasak/chdist-if-migrated
0new file mode 10075581new file mode 100755
index 0000000..dfd7778
--- /dev/null
+++ b/rbasak/chdist-if-migrated
@@ -0,0 +1,73 @@
1#!/bin/sh
2set -e
3
4# Author: Robie Basak <robie.basak@canonical.com>
5
6# Usage:
7# $0 <release> <try_file> apt-get|apt-cache <parameter...>
8#
9# Where try_file is the output line from update_output.txt starting
10# "Trying easy from autohinter:" specifying a list of source packages we
11# propose to migrate to the release pocket.
12#
13# Example use:
14#
15# Get apt to try simulating the installation of <package> following the
16# proposed migration:
17# $0 try-file apt-get -s install <package>
18
19# Prerequisites:
20# chdist configured and updated with the following dists:
21# ${release}: only the release pocket included
22# ${release}-proposed: both release and proposed pockets included
23# ${release}-proposed-only: only the proposed pocket included
24
25# For debian-installer support, add to each of
26# ~/.chdist/${release}{,-proposed,-proposed-only}/sources.list:
27#
28# deb http://archive.ubuntu.com/ubuntu ${release} main/debian-installer
29#
30# then update all chdists, and use something like:
31# $0 try-file apt-get -s install debian-installer-udebs
32
33
34release=$1
35try_file=$2
36command=$3
37shift 3
38
39tmpdir=`mktemp -d`
40cleanup() { rm -rf "$tmpdir"; }
41trap cleanup EXIT
42
43binaries() {
44 chdist_dist=$1
45 cat "$try_file"|sed 's/^Trying easy from autohinter: //g'|tr ' ' "\n"|cut -d/ -f1|xargs -P32 -n1 sh -c 'for p in "$@"; do chdist apt-cache '$chdist_dist' showsrc --only-source $p|grep-dctrl -nsPackage-List .|awk '\''{print $1}'\''|sed '\''/^$/d'\'';done' --
46}
47
48proposed_single_line_binaries=`binaries $release-proposed-only|tr "\n" ' '`
49release_single_line_binaries=`binaries $release|tr "\n" ' '`
50
51cat > "$tmpdir/preferences" <<EOT
52Package: *
53Pin: release a=${release}-proposed
54Pin-Priority: -1
55Explanation: cannot install from proposed except where we say so
56
57Package: $release_single_line_binaries
58Pin: release a=${release}
59Pin-Priority: -1
60Explanation: binaries produced by the release pocket version of the source package would be removed and therefore not available
61
62Package: $proposed_single_line_binaries
63Pin: release a=${release}
64Pin-Priority: -1
65Explanation: binaries being migrated into the release pocket would be elided by their replacements and therefore not available
66
67Package: $proposed_single_line_binaries
68Pin: release a=${release}-proposed
69Pin-Priority: 999
70Explanation: these binaries are proposed to be migrated into the release pocket so would be available
71EOT
72
73chdist "$command" ${release}-proposed -o "Dir::Etc::Preferences=$tmpdir/preferences" "$@"
diff --git a/rbasak/standup_timer.py b/rbasak/standup_timer.py
0new file mode 10064474new file mode 100644
index 0000000..638952b
--- /dev/null
+++ b/rbasak/standup_timer.py
@@ -0,0 +1,44 @@
1import tkinter
2import tkinter.font
3
4
5WINDOW_Y_SCALE = 0.90
6
7
8class StandupTimer(tkinter.Frame):
9 ALLOTTED_TIME = 60
10
11 def __init__(self, master=None):
12 super().__init__(master)
13 self._remaining_time = 0
14
15 self._label_font = tkinter.font.Font(self.master)
16 self._label = tkinter.Label(self, text=str(self._remaining_time))
17 self._label.config(font=self._label_font, bg="#ffffff")
18 self._label.pack(fill=tkinter.BOTH, expand=tkinter.YES)
19 self.pack()
20 self.master.bind("<Configure>", self._resize)
21 self.master.bind("<Key-space>", self._reset)
22 self.winfo_toplevel().after(1000, self._decrement)
23
24 def _decrement(self):
25 if self._remaining_time > 0:
26 self._remaining_time -= 1
27 self._label.config(text=str(self._remaining_time))
28 self.winfo_toplevel().after(1000, self._decrement)
29
30 def _resize(self, event):
31 self._label_font["size"] = int(
32 self._label.winfo_height() * WINDOW_Y_SCALE
33 )
34
35 def _reset(self, event):
36 self._remaining_time = self.ALLOTTED_TIME
37 self._label.config(text=str(self._remaining_time))
38
39
40if __name__ == "__main__":
41 root = tkinter.Tk()
42 root.config(bg="#ffffff")
43 app = StandupTimer(master=root)
44 app.mainloop()
diff --git a/rma b/rma
0deleted file mode 10075545deleted file mode 100755
index 7d455e2..0000000
--- a/rma
+++ /dev/null
@@ -1,3 +0,0 @@
1#!/bin/bash
2rmadison -u debian $1 &
3rmadison $1
diff --git a/uscards/AUTHORS.md b/uscards/AUTHORS.md
4new file mode 1006440new file mode 100644
index 0000000..9712ae3
--- /dev/null
+++ b/uscards/AUTHORS.md
@@ -0,0 +1 @@
1Bryce Harrington <bryce@canonical.com>
diff --git a/uscards/LICENSE.AGPL b/uscards/LICENSE.AGPL
0new file mode 1006442new file mode 100644
index 0000000..4ec8c3f
--- /dev/null
+++ b/uscards/LICENSE.AGPL
@@ -0,0 +1,619 @@
1 GNU AFFERO GENERAL PUBLIC LICENSE
2 Version 3, 19 November 2007
3
4 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5 Everyone is permitted to copy and distribute verbatim copies
6 of this license document, but changing it is not allowed.
7
8 Preamble
9
10 The GNU Affero General Public License is a free, copyleft license for
11software and other kinds of works, specifically designed to ensure
12cooperation with the community in the case of network server software.
13
14 The licenses for most software and other practical works are designed
15to take away your freedom to share and change the works. By contrast,
16our General Public Licenses are intended to guarantee your freedom to
17share and change all versions of a program--to make sure it remains free
18software for all its users.
19
20 When we speak of free software, we are referring to freedom, not
21price. Our General Public Licenses are designed to make sure that you
22have the freedom to distribute copies of free software (and charge for
23them if you wish), that you receive source code or can get it if you
24want it, that you can change the software or use pieces of it in new
25free programs, and that you know you can do these things.
26
27 Developers that use our General Public Licenses protect your rights
28with two steps: (1) assert copyright on the software, and (2) offer
29you this License which gives you legal permission to copy, distribute
30and/or modify the software.
31
32 A secondary benefit of defending all users' freedom is that
33improvements made in alternate versions of the program, if they
34receive widespread use, become available for other developers to
35incorporate. Many developers of free software are heartened and
36encouraged by the resulting cooperation. However, in the case of
37software used on network servers, this result may fail to come about.
38The GNU General Public License permits making a modified version and
39letting the public access it on a server without ever releasing its
40source code to the public.
41
42 The GNU Affero General Public License is designed specifically to
43ensure that, in such cases, the modified source code becomes available
44to the community. It requires the operator of a network server to
45provide the source code of the modified version running there to the
46users of that server. Therefore, public use of a modified version, on
47a publicly accessible server, gives the public access to the source
48code of the modified version.
49
50 An older license, called the Affero General Public License and
51published by Affero, was designed to accomplish similar goals. This is
52a different license, not a version of the Affero GPL, but Affero has
53released a new version of the Affero GPL which permits relicensing under
54this license.
55
56 The precise terms and conditions for copying, distribution and
57modification follow.
58
59 TERMS AND CONDITIONS
60
61 0. Definitions.
62
63 "This License" refers to version 3 of the GNU Affero General Public License.
64
65 "Copyright" also means copyright-like laws that apply to other kinds of
66works, such as semiconductor masks.
67
68 "The Program" refers to any copyrightable work licensed under this
69License. Each licensee is addressed as "you". "Licensees" and
70"recipients" may be individuals or organizations.
71
72 To "modify" a work means to copy from or adapt all or part of the work
73in a fashion requiring copyright permission, other than the making of an
74exact copy. The resulting work is called a "modified version" of the
75earlier work or a work "based on" the earlier work.
76
77 A "covered work" means either the unmodified Program or a work based
78on the Program.
79
80 To "propagate" a work means to do anything with it that, without
81permission, would make you directly or secondarily liable for
82infringement under applicable copyright law, except executing it on a
83computer or modifying a private copy. Propagation includes copying,
84distribution (with or without modification), making available to the
85public, and in some countries other activities as well.
86
87 To "convey" a work means any kind of propagation that enables other
88parties to make or receive copies. Mere interaction with a user through
89a computer network, with no transfer of a copy, is not conveying.
90
91 An interactive user interface displays "Appropriate Legal Notices"
92to the extent that it includes a convenient and prominently visible
93feature that (1) displays an appropriate copyright notice, and (2)
94tells the user that there is no warranty for the work (except to the
95extent that warranties are provided), that licensees may convey the
96work under this License, and how to view a copy of this License. If
97the interface presents a list of user commands or options, such as a
98menu, a prominent item in the list meets this criterion.
99
100 1. Source Code.
101
102 The "source code" for a work means the preferred form of the work
103for making modifications to it. "Object code" means any non-source
104form of a work.
105
106 A "Standard Interface" means an interface that either is an official
107standard defined by a recognized standards body, or, in the case of
108interfaces specified for a particular programming language, one that
109is widely used among developers working in that language.
110
111 The "System Libraries" of an executable work include anything, other
112than the work as a whole, that (a) is included in the normal form of
113packaging a Major Component, but which is not part of that Major
114Component, and (b) serves only to enable use of the work with that
115Major Component, or to implement a Standard Interface for which an
116implementation is available to the public in source code form. A
117"Major Component", in this context, means a major essential component
118(kernel, window system, and so on) of the specific operating system
119(if any) on which the executable work runs, or a compiler used to
120produce the work, or an object code interpreter used to run it.
121
122 The "Corresponding Source" for a work in object code form means all
123the source code needed to generate, install, and (for an executable
124work) run the object code and to modify the work, including scripts to
125control those activities. However, it does not include the work's
126System Libraries, or general-purpose tools or generally available free
127programs which are used unmodified in performing those activities but
128which are not part of the work. For example, Corresponding Source
129includes interface definition files associated with source files for
130the work, and the source code for shared libraries and dynamically
131linked subprograms that the work is specifically designed to require,
132such as by intimate data communication or control flow between those
133subprograms and other parts of the work.
134
135 The Corresponding Source need not include anything that users
136can regenerate automatically from other parts of the Corresponding
137Source.
138
139 The Corresponding Source for a work in source code form is that
140same work.
141
142 2. Basic Permissions.
143
144 All rights granted under this License are granted for the term of
145copyright on the Program, and are irrevocable provided the stated
146conditions are met. This License explicitly affirms your unlimited
147permission to run the unmodified Program. The output from running a
148covered work is covered by this License only if the output, given its
149content, constitutes a covered work. This License acknowledges your
150rights of fair use or other equivalent, as provided by copyright law.
151
152 You may make, run and propagate covered works that you do not
153convey, without conditions so long as your license otherwise remains
154in force. You may convey covered works to others for the sole purpose
155of having them make modifications exclusively for you, or provide you
156with facilities for running those works, provided that you comply with
157the terms of this License in conveying all material for which you do
158not control copyright. Those thus making or running the covered works
159for you must do so exclusively on your behalf, under your direction
160and control, on terms that prohibit them from making any copies of
161your copyrighted material outside their relationship with you.
162
163 Conveying under any other circumstances is permitted solely under
164the conditions stated below. Sublicensing is not allowed; section 10
165makes it unnecessary.
166
167 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168
169 No covered work shall be deemed part of an effective technological
170measure under any applicable law fulfilling obligations under article
17111 of the WIPO copyright treaty adopted on 20 December 1996, or
172similar laws prohibiting or restricting circumvention of such
173measures.
174
175 When you convey a covered work, you waive any legal power to forbid
176circumvention of technological measures to the extent such circumvention
177is effected by exercising rights under this License with respect to
178the covered work, and you disclaim any intention to limit operation or
179modification of the work as a means of enforcing, against the work's
180users, your or third parties' legal rights to forbid circumvention of
181technological measures.
182
183 4. Conveying Verbatim Copies.
184
185 You may convey verbatim copies of the Program's source code as you
186receive it, in any medium, provided that you conspicuously and
187appropriately publish on each copy an appropriate copyright notice;
188keep intact all notices stating that this License and any
189non-permissive terms added in accord with section 7 apply to the code;
190keep intact all notices of the absence of any warranty; and give all
191recipients a copy of this License along with the Program.
192
193 You may charge any price or no price for each copy that you convey,
194and you may offer support or warranty protection for a fee.
195
196 5. Conveying Modified Source Versions.
197
198 You may convey a work based on the Program, or the modifications to
199produce it from the Program, in the form of source code under the
200terms of section 4, provided that you also meet all of these conditions:
201
202 a) The work must carry prominent notices stating that you modified
203 it, and giving a relevant date.
204
205 b) The work must carry prominent notices stating that it is
206 released under this License and any conditions added under section
207 7. This requirement modifies the requirement in section 4 to
208 "keep intact all notices".
209
210 c) You must license the entire work, as a whole, under this
211 License to anyone who comes into possession of a copy. This
212 License will therefore apply, along with any applicable section 7
213 additional terms, to the whole of the work, and all its parts,
214 regardless of how they are packaged. This License gives no
215 permission to license the work in any other way, but it does not
216 invalidate such permission if you have separately received it.
217
218 d) If the work has interactive user interfaces, each must display
219 Appropriate Legal Notices; however, if the Program has interactive
220 interfaces that do not display Appropriate Legal Notices, your
221 work need not make them do so.
222
223 A compilation of a covered work with other separate and independent
224works, which are not by their nature extensions of the covered work,
225and which are not combined with it such as to form a larger program,
226in or on a volume of a storage or distribution medium, is called an
227"aggregate" if the compilation and its resulting copyright are not
228used to limit the access or legal rights of the compilation's users
229beyond what the individual works permit. Inclusion of a covered work
230in an aggregate does not cause this License to apply to the other
231parts of the aggregate.
232
233 6. Conveying Non-Source Forms.
234
235 You may convey a covered work in object code form under the terms
236of sections 4 and 5, provided that you also convey the
237machine-readable Corresponding Source under the terms of this License,
238in one of these ways:
239
240 a) Convey the object code in, or embodied in, a physical product
241 (including a physical distribution medium), accompanied by the
242 Corresponding Source fixed on a durable physical medium
243 customarily used for software interchange.
244
245 b) Convey the object code in, or embodied in, a physical product
246 (including a physical distribution medium), accompanied by a
247 written offer, valid for at least three years and valid for as
248 long as you offer spare parts or customer support for that product
249 model, to give anyone who possesses the object code either (1) a
250 copy of the Corresponding Source for all the software in the
251 product that is covered by this License, on a durable physical
252 medium customarily used for software interchange, for a price no
253 more than your reasonable cost of physically performing this
254 conveying of source, or (2) access to copy the
255 Corresponding Source from a network server at no charge.
256
257 c) Convey individual copies of the object code with a copy of the
258 written offer to provide the Corresponding Source. This
259 alternative is allowed only occasionally and noncommercially, and
260 only if you received the object code with such an offer, in accord
261 with subsection 6b.
262
263 d) Convey the object code by offering access from a designated
264 place (gratis or for a charge), and offer equivalent access to the
265 Corresponding Source in the same way through the same place at no
266 further charge. You need not require recipients to copy the
267 Corresponding Source along with the object code. If the place to
268 copy the object code is a network server, the Corresponding Source
269 may be on a different server (operated by you or a third party)
270 that supports equivalent copying facilities, provided you maintain
271 clear directions next to the object code saying where to find the
272 Corresponding Source. Regardless of what server hosts the
273 Corresponding Source, you remain obligated to ensure that it is
274 available for as long as needed to satisfy these requirements.
275
276 e) Convey the object code using peer-to-peer transmission, provided
277 you inform other peers where the object code and Corresponding
278 Source of the work are being offered to the general public at no
279 charge under subsection 6d.
280
281 A separable portion of the object code, whose source code is excluded
282from the Corresponding Source as a System Library, need not be
283included in conveying the object code work.
284
285 A "User Product" is either (1) a "consumer product", which means any
286tangible personal property which is normally used for personal, family,
287or household purposes, or (2) anything designed or sold for incorporation
288into a dwelling. In determining whether a product is a consumer product,
289doubtful cases shall be resolved in favor of coverage. For a particular
290product received by a particular user, "normally used" refers to a
291typical or common use of that class of product, regardless of the status
292of the particular user or of the way in which the particular user
293actually uses, or expects or is expected to use, the product. A product
294is a consumer product regardless of whether the product has substantial
295commercial, industrial or non-consumer uses, unless such uses represent
296the only significant mode of use of the product.
297
298 "Installation Information" for a User Product means any methods,
299procedures, authorization keys, or other information required to install
300and execute modified versions of a covered work in that User Product from
301a modified version of its Corresponding Source. The information must
302suffice to ensure that the continued functioning of the modified object
303code is in no case prevented or interfered with solely because
304modification has been made.
305
306 If you convey an object code work under this section in, or with, or
307specifically for use in, a User Product, and the conveying occurs as
308part of a transaction in which the right of possession and use of the
309User Product is transferred to the recipient in perpetuity or for a
310fixed term (regardless of how the transaction is characterized), the
311Corresponding Source conveyed under this section must be accompanied
312by the Installation Information. But this requirement does not apply
313if neither you nor any third party retains the ability to install
314modified object code on the User Product (for example, the work has
315been installed in ROM).
316
317 The requirement to provide Installation Information does not include a
318requirement to continue to provide support service, warranty, or updates
319for a work that has been modified or installed by the recipient, or for
320the User Product in which it has been modified or installed. Access to a
321network may be denied when the modification itself materially and
322adversely affects the operation of the network or violates the rules and
323protocols for communication across the network.
324
325 Corresponding Source conveyed, and Installation Information provided,
326in accord with this section must be in a format that is publicly
327documented (and with an implementation available to the public in
328source code form), and must require no special password or key for
329unpacking, reading or copying.
330
331 7. Additional Terms.
332
333 "Additional permissions" are terms that supplement the terms of this
334License by making exceptions from one or more of its conditions.
335Additional permissions that are applicable to the entire Program shall
336be treated as though they were included in this License, to the extent
337that they are valid under applicable law. If additional permissions
338apply only to part of the Program, that part may be used separately
339under those permissions, but the entire Program remains governed by
340this License without regard to the additional permissions.
341
342 When you convey a copy of a covered work, you may at your option
343remove any additional permissions from that copy, or from any part of
344it. (Additional permissions may be written to require their own
345removal in certain cases when you modify the work.) You may place
346additional permissions on material, added by you to a covered work,
347for which you have or can give appropriate copyright permission.
348
349 Notwithstanding any other provision of this License, for material you
350add to a covered work, you may (if authorized by the copyright holders of
351that material) supplement the terms of this License with terms:
352
353 a) Disclaiming warranty or limiting liability differently from the
354 terms of sections 15 and 16 of this License; or
355
356 b) Requiring preservation of specified reasonable legal notices or
357 author attributions in that material or in the Appropriate Legal
358 Notices displayed by works containing it; or
359
360 c) Prohibiting misrepresentation of the origin of that material, or
361 requiring that modified versions of such material be marked in
362 reasonable ways as different from the original version; or
363
364 d) Limiting the use for publicity purposes of names of licensors or
365 authors of the material; or
366
367 e) Declining to grant rights under trademark law for use of some
368 trade names, trademarks, or service marks; or
369
370 f) Requiring indemnification of licensors and authors of that
371 material by anyone who conveys the material (or modified versions of
372 it) with contractual assumptions of liability to the recipient, for
373 any liability that these contractual assumptions directly impose on
374 those licensors and authors.
375
376 All other non-permissive additional terms are considered "further
377restrictions" within the meaning of section 10. If the Program as you
378received it, or any part of it, contains a notice stating that it is
379governed by this License along with a term that is a further
380restriction, you may remove that term. If a license document contains
381a further restriction but permits relicensing or conveying under this
382License, you may add to a covered work material governed by the terms
383of that license document, provided that the further restriction does
384not survive such relicensing or conveying.
385
386 If you add terms to a covered work in accord with this section, you
387must place, in the relevant source files, a statement of the
388additional terms that apply to those files, or a notice indicating
389where to find the applicable terms.
390
391 Additional terms, permissive or non-permissive, may be stated in the
392form of a separately written license, or stated as exceptions;
393the above requirements apply either way.
394
395 8. Termination.
396
397 You may not propagate or modify a covered work except as expressly
398provided under this License. Any attempt otherwise to propagate or
399modify it is void, and will automatically terminate your rights under
400this License (including any patent licenses granted under the third
401paragraph of section 11).
402
403 However, if you cease all violation of this License, then your
404license from a particular copyright holder is reinstated (a)
405provisionally, unless and until the copyright holder explicitly and
406finally terminates your license, and (b) permanently, if the copyright
407holder fails to notify you of the violation by some reasonable means
408prior to 60 days after the cessation.
409
410 Moreover, your license from a particular copyright holder is
411reinstated permanently if the copyright holder notifies you of the
412violation by some reasonable means, this is the first time you have
413received notice of violation of this License (for any work) from that
414copyright holder, and you cure the violation prior to 30 days after
415your receipt of the notice.
416
417 Termination of your rights under this section does not terminate the
418licenses of parties who have received copies or rights from you under
419this License. If your rights have been terminated and not permanently
420reinstated, you do not qualify to receive new licenses for the same
421material under section 10.
422
423 9. Acceptance Not Required for Having Copies.
424
425 You are not required to accept this License in order to receive or
426run a copy of the Program. Ancillary propagation of a covered work
427occurring solely as a consequence of using peer-to-peer transmission
428to receive a copy likewise does not require acceptance. However,
429nothing other than this License grants you permission to propagate or
430modify any covered work. These actions infringe copyright if you do
431not accept this License. Therefore, by modifying or propagating a
432covered work, you indicate your acceptance of this License to do so.
433
434 10. Automatic Licensing of Downstream Recipients.
435
436 Each time you convey a covered work, the recipient automatically
437receives a license from the original licensors, to run, modify and
438propagate that work, subject to this License. You are not responsible
439for enforcing compliance by third parties with this License.
440
441 An "entity transaction" is a transaction transferring control of an
442organization, or substantially all assets of one, or subdividing an
443organization, or merging organizations. If propagation of a covered
444work results from an entity transaction, each party to that
445transaction who receives a copy of the work also receives whatever
446licenses to the work the party's predecessor in interest had or could
447give under the previous paragraph, plus a right to possession of the
448Corresponding Source of the work from the predecessor in interest, if
449the predecessor has it or can get it with reasonable efforts.
450
451 You may not impose any further restrictions on the exercise of the
452rights granted or affirmed under this License. For example, you may
453not impose a license fee, royalty, or other charge for exercise of
454rights granted under this License, and you may not initiate litigation
455(including a cross-claim or counterclaim in a lawsuit) alleging that
456any patent claim is infringed by making, using, selling, offering for
457sale, or importing the Program or any portion of it.
458
459 11. Patents.
460
461 A "contributor" is a copyright holder who authorizes use under this
462License of the Program or a work on which the Program is based. The
463work thus licensed is called the contributor's "contributor version".
464
465 A contributor's "essential patent claims" are all patent claims
466owned or controlled by the contributor, whether already acquired or
467hereafter acquired, that would be infringed by some manner, permitted
468by this License, of making, using, or selling its contributor version,
469but do not include claims that would be infringed only as a
470consequence of further modification of the contributor version. For
471purposes of this definition, "control" includes the right to grant
472patent sublicenses in a manner consistent with the requirements of
473this License.
474
475 Each contributor grants you a non-exclusive, worldwide, royalty-free
476patent license under the contributor's essential patent claims, to
477make, use, sell, offer for sale, import and otherwise run, modify and
478propagate the contents of its contributor version.
479
480 In the following three paragraphs, a "patent license" is any express
481agreement or commitment, however denominated, not to enforce a patent
482(such as an express permission to practice a patent or covenant not to
483sue for patent infringement). To "grant" such a patent license to a
484party means to make such an agreement or commitment not to enforce a
485patent against the party.
486
487 If you convey a covered work, knowingly relying on a patent license,
488and the Corresponding Source of the work is not available for anyone
489to copy, free of charge and under the terms of this License, through a
490publicly available network server or other readily accessible means,
491then you must either (1) cause the Corresponding Source to be so
492available, or (2) arrange to deprive yourself of the benefit of the
493patent license for this particular work, or (3) arrange, in a manner
494consistent with the requirements of this License, to extend the patent
495license to downstream recipients. "Knowingly relying" means you have
496actual knowledge that, but for the patent license, your conveying the
497covered work in a country, or your recipient's use of the covered work
498in a country, would infringe one or more identifiable patents in that
499country that you have reason to believe are valid.
500
501 If, pursuant to or in connection with a single transaction or
502arrangement, you convey, or propagate by procuring conveyance of, a
503covered work, and grant a patent license to some of the parties
504receiving the covered work authorizing them to use, propagate, modify
505or convey a specific copy of the covered work, then the patent license
506you grant is automatically extended to all recipients of the covered
507work and works based on it.
508
509 A patent license is "discriminatory" if it does not include within
510the scope of its coverage, prohibits the exercise of, or is
511conditioned on the non-exercise of one or more of the rights that are
512specifically granted under this License. You may not convey a covered
513work if you are a party to an arrangement with a third party that is
514in the business of distributing software, under which you make payment
515to the third party based on the extent of your activity of conveying
516the work, and under which the third party grants, to any of the
517parties who would receive the covered work from you, a discriminatory
518patent license (a) in connection with copies of the covered work
519conveyed by you (or copies made from those copies), or (b) primarily
520for and in connection with specific products or compilations that
521contain the covered work, unless you entered into that arrangement,
522or that patent license was granted, prior to 28 March 2007.
523
524 Nothing in this License shall be construed as excluding or limiting
525any implied license or other defenses to infringement that may
526otherwise be available to you under applicable patent law.
527
528 12. No Surrender of Others' Freedom.
529
530 If conditions are imposed on you (whether by court order, agreement or
531otherwise) that contradict the conditions of this License, they do not
532excuse you from the conditions of this License. If you cannot convey a
533covered work so as to satisfy simultaneously your obligations under this
534License and any other pertinent obligations, then as a consequence you may
535not convey it at all. For example, if you agree to terms that obligate you
536to collect a royalty for further conveying from those to whom you convey
537the Program, the only way you could satisfy both those terms and this
538License would be to refrain entirely from conveying the Program.
539
540 13. Remote Network Interaction; Use with the GNU General Public License.
541
542 Notwithstanding any other provision of this License, if you modify the
543Program, your modified version must prominently offer all users
544interacting with it remotely through a computer network (if your version
545supports such interaction) an opportunity to receive the Corresponding
546Source of your version by providing access to the Corresponding Source
547from a network server at no charge, through some standard or customary
548means of facilitating copying of software. This Corresponding Source
549shall include the Corresponding Source for any work covered by version 3
550of the GNU General Public License that is incorporated pursuant to the
551following paragraph.
552
553 Notwithstanding any other provision of this License, you have
554permission to link or combine any covered work with a work licensed
555under version 3 of the GNU General Public License into a single
556combined work, and to convey the resulting work. The terms of this
557License will continue to apply to the part which is the covered work,
558but the work with which it is combined will remain governed by version
5593 of the GNU General Public License.
560
561 14. Revised Versions of this License.
562
563 The Free Software Foundation may publish revised and/or new versions of
564the GNU Affero General Public License from time to time. Such new versions
565will be similar in spirit to the present version, but may differ in detail to
566address new problems or concerns.
567
568 Each version is given a distinguishing version number. If the
569Program specifies that a certain numbered version of the GNU Affero General
570Public License "or any later version" applies to it, you have the
571option of following the terms and conditions either of that numbered
572version or of any later version published by the Free Software
573Foundation. If the Program does not specify a version number of the
574GNU Affero General Public License, you may choose any version ever published
575by the Free Software Foundation.
576
577 If the Program specifies that a proxy can decide which future
578versions of the GNU Affero General Public License can be used, that proxy's
579public statement of acceptance of a version permanently authorizes you
580to choose that version for the Program.
581
582 Later license versions may give you additional or different
583permissions. However, no additional obligations are imposed on any
584author or copyright holder as a result of your choosing to follow a
585later version.
586
587 15. Disclaimer of Warranty.
588
589 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597
598 16. Limitation of Liability.
599
600 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608SUCH DAMAGES.
609
610 17. Interpretation of Sections 15 and 16.
611
612 If the disclaimer of warranty and limitation of liability provided
613above cannot be given local legal effect according to their terms,
614reviewing courts shall apply local law that most closely approximates
615an absolute waiver of all civil liability in connection with the
616Program, unless a warranty or assumption of liability accompanies a
617copy of the Program in return for a fee.
618
619 END OF TERMS AND CONDITIONS
diff --git a/uscards/README b/uscards/README
0new file mode 120000620new file mode 120000
index 0000000..42061c0
--- /dev/null
+++ b/uscards/README
@@ -0,0 +1 @@
1README.md
0\ No newline at end of file2\ No newline at end of file
diff --git a/uscards/README.md b/uscards/README.md
1new file mode 1006443new file mode 100644
index 0000000..3f25112
--- /dev/null
+++ b/uscards/README.md
@@ -0,0 +1,26 @@
1# USCARDS #
2
3Ubuntu Server Trello Cards CLI
4
5This collection of scripts provide convenience CLIs to the Ubuntu Server
6team's trello boards. Each script maps to a specific board and
7implements the team's particular policy logic for that board.
8
9## Use cases ##
10
11 - Get authenticated
12 - List all my open merge cards
13 - List all cards for a specified board and/or list
14 - Add a new card to the TODO list on the roadmap
15 - Create triage and migration duty cards for a given user
16 - Move your triage and migration duty tasks from TODO to Doing
17 - Move your triage and migration duty tasks from Doing to Done
18 - Update the label on a package's card in Roadmap: merges
19 - Append to the description of a card in Roadmap: merges
20
21## Configuration ##
22
23Create ~/.config/uscards.conf with contents such as:
24
25 # USCards configuration
26 TRELLO_USERNAME="myusername"
diff --git a/uscards/docs/design.txt b/uscards/docs/design.txt
0new file mode 10064427new file mode 100644
index 0000000..0fe9708
--- /dev/null
+++ b/uscards/docs/design.txt
@@ -0,0 +1,100 @@
1 # Design of Usdaily #
2
3The purpose of usdaily is to give a super convenient way for the server
4team's "Squeaky Wheel" squad to interact with the Trello cards that make
5up the main part of their workflow, from the command line. In
6particular, it's envisioned this will facilitate further scripting and
7automation of workflows such as merges, SRUs, proposed migration and so
8on.
9
10This is not intended to be a general utility, just be a convenience
11wrapper around the actual general utility, 'so-trello'. As well, we're
12not intending to implement the full range of the Trello web
13functionality, and accept that certain tasks may just be a lot simpler
14or more convenient to do from the Trello website.
15
16
17### Boards ###
18
19The server team maintains a number of Trello boards, but we're mainly
20concerned with these boards:
21
22 - Daily
23 - [Groovy] Merges Schedule (and Package Merge Status board)
24 - Proposed Migration
25 - SRU
26
27These boards have repetitive aspects to their workflows, which could be
28ameniable to adding trello board update calls as part of a script.
29Development-focused boards like the git-ubuntu roadmap board, that don't
30have repetitive workflow aspects, may not benefit as much from CLI tools
31and for this reason are not included. Backlog boards are by definition
32rarely interacted with, and thus not included for similar reasons.
33
34Each board is implemented as a separate script. This is for two
35reasons: First, it avoids needing to have the user specify the board to
36operate on; and second, each board may have some unique elements to how
37they're used which is best to encapsulate.
38
39Note that there are actually two boards for merges - one that acts more
40like a schedule to plan when merges should happen, and a second that
41registers when merges are available to be done. In this case, the two
42boards work like different facets on the same workflow - i.e. when
43updating a card on one merge board there may be a corresponding card on
44the other board that would need similarly updated, so a single script is
45designed to interact with both boards.
46
47
48### Operations ###
49
50All of the scripts will support certain core operations:
51
52 - List cards
53 - Add a card
54 - Start a card
55 - Mark a card as ready for review
56 - Add a comment to a card
57 - Finish a card
58
59In some cases, these operations result in moving a card from one list to
60another; in other cases it is achieved via labels.
61
62Except for the 'list' command, all operations will take a card-id.
63so-trello expects the card to be identified by it's textual title, which
64makes sense from a user perspective, but given that the title can be
65edited and changed at will, it may be difficult to rely on so commands
66should also accept card-id's derived from the card URL. Or if no
67card-id is specified, the operation should prompt the user with a list
68of plausible choices to select from.
69
70There are also some board-specific functionality, that the scripts are
71intended to facilitate:
72
73 - The Merges Schedule are organized with a list of cards per month, so
74 being able to see still-active merges for a range of months
75 (e.g. current month and before) is useful.
76
77 - The Proposed Migration board uses the first card in some lists as
78 documentation for the list.
79
80
81### Coding Approach ###
82
83'uscards' is kept in Bash deliberately, both to keep implementation
84more accessible to team members, and to encourage keeping implementation
85streamlined and simple. Apart from 'so-trello' (which can be easily
86installed via snap), there are no installation dependencies, and the
87individual scripts can be self-contained as single files.
88
89The use of 'so-trello' was motivated by these design constraints,
90however other options were considered including coding in python and
91using the python-trello module. An earlier effort, 'postcard' was
92written purely in Python, but its attempt to provide both the frontend
93CLI and the backend Trello interfacing, resulted in unnecessary
94complexity.
95
96Another option considered was Report2Trello, a python toolset built
97around the python-trello module, focused on bulk-update use cases to
98generate and update boards based off of external data sources; 'uscards'
99focuses more on a command-line interface way of interacting with the
100boards.
diff --git a/uscards/install.sh b/uscards/install.sh
0new file mode 100755101new file mode 100755
index 0000000..c9bbaa3
--- /dev/null
+++ b/uscards/install.sh
@@ -0,0 +1,9 @@
1#!/bin/bash
2
3dest="${1:-/usr/local/bin/}"
4
5for script in scripts/*; do
6 if [ -x "${script}" ]; then
7 cp -v "${script}" "${dest}"
8 fi
9done
diff --git a/uscards/scripts/sru-board b/uscards/scripts/sru-board
0new file mode 10075510new file mode 100755
index 0000000..59eaef6
--- /dev/null
+++ b/uscards/scripts/sru-board
@@ -0,0 +1,158 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4# This script uses so-trello to update cards on the Ubuntu SRU
5# Trello board.
6#
7# Copyright (C) 2020 Bryce W. Harrington <bryce@bryceharrington.org>
8#
9# This program is free software: you can redistribute it and/or
10# modify it under the terms of the GNU Affero General Public
11# License as published by the Free Software Foundation, either
12# version 3 of the License, or (at your option) any later
13# version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23progname=$(basename "${0}")
24progdir=$(dirname "$(readlink -f "${0}")")
25scriptsdir="${progdir}/../scripts"
26
27usage() {
28 cat >&2 <<EOF
29Usage: ${progname} [options] <command [params]>
30
31Commands:
32 list Display your open cards
33 finish <card-title> Move card to Done column
34
35Options:
36 -h, --help This help listing
37EOF
38 exit $1
39}
40
41warn() {
42 echo "Warning: $1" 1>&2
43}
44
45die() {
46 echo "Error: $1" 1>&2
47 exit 1
48}
49
50card_id() {
51 local card="${1}"
52
53 if [[ "${card}" =~ "https://trello.com/"* ]]; then
54 # Looks like a card URL
55 echo "${card##*/}"
56 return 0
57 else
58 echo "${card}"
59 return 0
60 fi
61
62 warn "Could not parse card id from '${card}'"
63 return 1
64}
65
66
67#####################
68### Parse Options ###
69#####################
70
71parse_args() {
72 # Transform long options to short ones
73 for arg in "$@"; do
74 shift
75 case "${arg}" in
76 "--help") set -- "$@" "-h" ;;
77 "--*") usage 1 ;;
78 *) set -- "$@" "${arg}"
79 esac
80 done
81
82 # Parse short options
83 OPTIND=1
84 while getopts "h\?" opt $*; do
85 case "${opt}" in
86 h | \?) usage 0; ;;
87 * ) die "Unknown option '${opt}'" ;;
88 esac
89 done
90 shift $(expr ${OPTIND} - 1)
91}
92
93############
94### Main ###
95############
96
97function main() {
98 # Parse options
99 parse_args "$@" \
100 || die "Could not process options"
101 command="${1}" && shift
102 [ -n "${command}" ] || usage 1
103
104 if [ -e ~/.config/uscards.conf ]; then
105 source ~/.config/uscards.conf
106 elif [ -z "${TRELLO_USERNAME}" ]; then
107 die "TRELLO_USERNAME env var needs to be set to your trello account name"
108 fi
109
110 my_boards=$(so-trello member-boards)
111 sru_board_id=$(echo "${my_boards}" | grep "SRU" | cut -d' ' -f1)
112 sru_lists=$(so-trello board-lists --board "${sru_board_id}")
113
114 list_languishing=$(echo "${sru_lists}" | grep "Languishing" | cut -d' ' -f1)
115 list_todo=$(echo "${sru_lists}" | grep "To *Do" | cut -d' ' -f1)
116 list_doing=$(echo "${sru_lists}" | grep "Doing" | cut -d' ' -f1)
117 list_done=$(echo "${sru_lists}" | grep "Done" | cut -d' ' -f1)
118
119 case "${command}" in
120 list)
121 echo "Languishing:"
122 so-trello list-cards --list "${list_languishing}" | cut -d' ' -f2- | sed -e 's/^/* /'
123 echo
124
125 echo "To Do:"
126 so-trello list-cards --list "${list_todo}" | cut -d' ' -f2- | sed -e 's/^/* /'
127 echo
128
129 echo "Doing:"
130 so-trello list-cards --list "${list_doing}" | cut -d' ' -f2- | sed -e 's/^/* /'
131 echo
132 ;;
133
134 finish)
135 card=$(card_id "${1}")
136 [ -n "${card}" ] || usage 1
137
138 so-trello card-update --board "${sru_board_id}" \
139 --update-board "${sru_board_id}" --update-list "${list_done}" \
140 --card "${card}"
141 ;;
142
143 help)
144 usage 0
145 ;;
146 *)
147 usage 1
148 ;;
149 esac
150
151 # Subcommands to add:
152 # * start <card> # Moves from TODO column to the Doing column
153 # * finish <card> # Moves to the DONE column
154}
155
156if [[ "${0}" == "${BASH_SOURCE}" ]]; then
157 main "$@"
158fi
diff --git a/uscards/scripts/uscoredev b/uscards/scripts/uscoredev
0new file mode 100755159new file mode 100755
index 0000000..1f2688b
--- /dev/null
+++ b/uscards/scripts/uscoredev
@@ -0,0 +1,189 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4# This script uses so-trello to dump cards from a prospective
5# core-dev applicant's Trello board.
6#
7# Copyright (C) 2021 Bryce W. Harrington <bryce@canonical.com>
8#
9# This program is free software: you can redistribute it and/or
10# modify it under the terms of the GNU Affero General Public
11# License as published by the Free Software Foundation, either
12# version 3 of the License, or (at your option) any later
13# version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23progname=$(basename "${0}")
24progdir=$(dirname "$(readlink -f "${0}")")
25scriptsdir="${progdir}/../scripts"
26
27usage() {
28 cat >&2 <<EOF
29Usage: ${progname} [options]
30
31Commands:
32 list Generate a Moinmoin-style dump of cards from Trello
33 help This help listing
34
35Parameters:
36 <name> First part of board title, i.e. "<name> Core Dev Roadmap"
37
38Options:
39 -h, --help This help listing
40EOF
41 exit $1
42}
43
44warn() {
45 echo "Warning: $1" 1>&2
46}
47
48die() {
49 echo "Error: $1" 1>&2
50 exit 1
51}
52
53card_id() {
54 local card="${1}"
55
56 if [[ "${card}" =~ ${re_number} ]]; then
57 # Treat as the card id directly
58 echo "${card}"
59 return 0
60 elif [[ "${card}" =~ "https://trello.com/"* ]]; then
61 # Looks like a card URL
62 echo "${card##*/}"
63 return 0
64 fi
65
66 warn "Could not parse card id from '${card}'"
67 return 1
68}
69
70#####################
71### Parse Options ###
72#####################
73
74parse_args() {
75 # Transform long options to short ones
76 for arg in "$@"; do
77 shift
78 case "${arg}" in
79 "--help") set -- "$@" "-h" ;;
80 "--*") usage 1 ;;
81 *) set -- "$@" "${arg}"
82 esac
83 done
84
85 # Parse short options
86 OPTIND=1
87 while getopts "h\?" opt $*; do
88 case "${opt}" in
89 h | \?) usage 0; ;;
90 * ) die "Unknown option '${opt}'" ;;
91 esac
92 done
93 shift $(expr ${OPTIND} - 1)
94}
95
96############
97### Main ###
98############
99
100function main() {
101 # Parse options
102 parse_args "$@" \
103 || die "Could not process options"
104
105 if [ -e ~/.config/uscards.conf ]; then
106 source ~/.config/uscards.conf
107 elif [ -z "${TRELLO_USERNAME}" ]; then
108 die "TRELLO_USERNAME env var needs to be set to your trello account name"
109 fi
110
111 # Command to execute
112 command="${1}" && shift
113 [ -n "${command}" ] || usage 1
114
115 # Name and ID of the trello board
116 name="${@}"
117 my_boards=$(so-trello member-boards)
118 if [ -z "${name}" ]; then
119 num_boards=$(echo "${my_boards}" | grep -i "Core Dev Roadmap" | grep -v "Example" | wc -l)
120 if [ "${num_boards}" = 0 ]; then
121 # User has no core-dev roadmaps, so use the example
122 coredev_board_id=$(echo "${my_boards}" | grep -i "Example Core Dev Roadmap" | cut -d' ' -f1)
123 elif [ "${num_boards}" = "1" ]; then
124 # User has exactly one board (common case)
125 coredev_board_id=$(echo "${my_boards}" | grep -i "Core Dev Roadmap" | grep -v "Example" | cut -d' ' -f1)
126 else
127 # TODO: Present user with list to choose from
128 usage 1
129 fi
130 else
131 coredev_board_id=$(echo "${my_boards}" | grep -i "${name} Core Dev Roadmap" | cut -d' ' -f1)
132 fi
133 coredev_board_name=$(echo "${my_boards}" | grep -i "^${coredev_board_id}" | cut -d' ' -f2-)
134
135 # Column names
136 coredev_lists=$(so-trello board-lists --board "${coredev_board_id}")
137 selected_lists=$(echo "${coredev_lists}" | grep -v TODO | grep -v Doing)
138
139 case "${command}" in
140 list)
141 echo "== ${coredev_board_name/Core Dev Roadmap/Involvement} =="
142 echo
143 echo "=== Examples of my work ==="
144 echo
145 for list in $(echo "${selected_lists}" | cut -d' ' -f1); do
146 list_name=$(so-trello list --board "${coredev_board_id}" --list "${list}" | cut -d' ' -f2-)
147 cards=$(so-trello list-cards --list "${list}")
148 if [ -n "${cards}" ]; then
149 echo "${list_name}:"
150 for card_id in $(echo "${cards}" | cut -d' ' -f1); do
151 # Get the card's title
152 card_title=$(echo "${cards}" | grep "^${card_id}" | cut -d' ' -f2-)
153
154 # Extract work item link from card description
155 #echo "so-trello card --board \"${coredev_board_id}\" --card \"${card_id}\" --raw json | jq .desc"
156 card_desc=$(so-trello card --board "${coredev_board_id}" --card "${card_id}" --raw json | jq .desc)
157 link=$(echo "${card_desc//\"/}" | egrep -o 'https?://[^ ]+')
158 if [ -z "${link}" ]; then
159 echo " * ${card_title}"
160 else
161 link_number=$(echo "${link}" | egrep -o '[0-9]+$')
162 if [ -z "${link_number}" ]; then
163 link_text="link"
164 elif [[ "${link}" == *"+bug"* ]]; then
165 link_text="LP: #${link_number}"
166 elif [[ "${link}" == *"+merge"* ]]; then
167 link_text="MP: #${link_number}"
168 fi
169 echo " * ${card_title} - [[${link}|${link_text}]]"
170 fi
171 done
172 fi
173 echo
174 done
175 ;;
176
177 help)
178 usage 0
179 ;;
180 *)
181 usage 1
182 ;;
183 esac
184 exit
185}
186
187if [[ "${0}" == "${BASH_SOURCE}" ]]; then
188 main "$@"
189fi
diff --git a/uscards/scripts/usdaily b/uscards/scripts/usdaily
0new file mode 100755190new file mode 100755
index 0000000..04e156c
--- /dev/null
+++ b/uscards/scripts/usdaily
@@ -0,0 +1,186 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3#
4# This script uses so-trello to update cards on the Ubuntu Server team's
5# Daily Trello board.
6#
7# Copyright (C) 2020 Bryce W. Harrington <bryce@bryceharrington.org>
8#
9# This program is free software: you can redistribute it and/or
10# modify it under the terms of the GNU Affero General Public
11# License as published by the Free Software Foundation, either
12# version 3 of the License, or (at your option) any later
13# version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23progname=$(basename "${0}")
24progdir=$(dirname "$(readlink -f "${0}")")
25scriptsdir="${progdir}/../scripts"
26
27usage() {
28 cat >&2 <<EOF
29Usage: ${progname} [options] <command [params]>
30
31Commands:
32 list Display your open cards
33 add <card-title> Adds card with given title to TODO list
34 finish <card-id> Move card to Done column
35
36Options:
37 -h, --help This help listing
38EOF
39 exit $1
40}
41
42warn() {
43 echo "Warning: $1" 1>&2
44}
45
46die() {
47 echo "Error: $1" 1>&2
48 exit 1
49}
50
51card_id() {
52 local card="${1}"
53
54 if [[ "${card}" =~ "https://trello.com/"* ]]; then
55 # Looks like a card URL
56 echo "${card##*/}"
57 return 0
58 else
59 echo "${card}"
60 return 0
61 fi
62
63 warn "Could not parse card id from '${card}'"
64 return 1
65}
66
67#####################
68### Parse Options ###
69#####################
70
71parse_args() {
72 # Transform long options to short ones
73 for arg in "$@"; do
74 shift
75 case "${arg}" in
76 "--help") set -- "$@" "-h" ;;
77 "--*") usage 1 ;;
78 *) set -- "$@" "${arg}"
79 esac
80 done
81
82 # Parse short options
83 OPTIND=1
84 while getopts "h\?" opt $*; do
85 case "${opt}" in
86 h | \?) usage 0; ;;
87 * ) die "Unknown option '${opt}'" ;;
88 esac
89 done
90 shift $(expr ${OPTIND} - 1)
91}
92
93
94############
95### Main ###
96############
97
98function main() {
99 # Parse options and command
100 parse_args $@ \
101 || die "Could not process options"
102 command="${1}" && shift
103 [ -n "${command}" ] || usage 1
104
105 if [ -e ~/.config/uscards.conf ]; then
106 source ~/.config/uscards.conf
107 elif [ -z "${TRELLO_USERNAME}" ]; then
108 die "TRELLO_USERNAME env var needs to be set to your trello account name"
109 fi
110
111 my_boards=$(so-trello member-boards)
112 daily_board_id=$(echo "${my_boards}" | grep -i "Daily Ubuntu Server" | cut -d' ' -f1)
113 daily_lists=$(so-trello board-lists --board "${daily_board_id}")
114
115 list_todo=$(echo "${daily_lists}" | grep "TODO" | cut -d' ' -f1)
116 list_doing=$(echo "${daily_lists}" | grep -i "Doing" | cut -d' ' -f1)
117 list_review=$(echo "${daily_lists}" | grep -i "Review" | cut -d' ' -f1)
118 list_external_dependency=$(echo "${daily_lists}" | grep -i "EXTERNAL DEPENDENCY" | cut -d' ' -f1)
119 list_done=$(echo "${daily_lists}" | grep "Done" | cut -d' ' -f1)
120
121 # TODO: Verify the above actually exist
122
123 case "${command}" in
124 list)
125 echo "TODO:"
126 so-trello list-cards --list "${list_todo}" --is-member "${TRELLO_USERNAME}" | cut -d' ' -f2- | sed -e 's/^/* /'
127 echo
128
129 echo "Doing:"
130 so-trello list-cards --list "${list_doing}" --is-member "${TRELLO_USERNAME}" | cut -d' ' -f2- | sed -e 's/^/* /'
131 echo
132
133 echo "Reviews:"
134 so-trello list-cards --list "${list_review}" --is-member "${TRELLO_USERNAME}" | cut -d' ' -f2- | sed -e 's/^/* /'
135 echo
136 ;;
137
138 add)
139 local name="${@}"
140 [ -n "${name}" ] || usage 1
141
142 # By default, use the "TODO" list
143 local list="TODO"
144
145 # Check if stdin comes from terminal or a pipe
146 if [ -t 0 ]; then
147 # Terminal. Just add card with title.
148 so-trello list-addcard \
149 --board "${daily_board_id}" \
150 --list "${list}" \
151 --card-name "${name}"
152 echo "${name}:\n${description}"
153 else
154 # Pipe. Use stdin as the description.
155 local description=$(cat <&0)
156 so-trello list-addcard \
157 --board "${daily_board_id}" \
158 --list "${list}" \
159 --card-name "${name}" \
160 --card-desc "${description}"
161 echo "${name}:"
162 echo "${description}"
163 fi
164 ;;
165
166 finish)
167 card=$(card_id "${1}")
168 [ -n "${card}" ] || usage 1
169
170 so-trello card-update --board "${daily_board_id}" \
171 --update-board "${daily_board_id}" --update-list "${list_done}" \
172 --card "${card}"
173 ;;
174
175 help)
176 usage 0
177 ;;
178 *)
179 usage 1
180 ;;
181 esac
182}
183
184if [[ "${0}" == "${BASH_SOURCE}" ]]; then
185 main "$@"
186fi
diff --git a/uscards/scripts/usmerges b/uscards/scripts/usmerges
0new file mode 100755187new file mode 100755
index 0000000..0aa4d6a
--- /dev/null
+++ b/uscards/scripts/usmerges
@@ -0,0 +1,194 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3
4# This script uses so-trello to update cards on the Ubuntu Server team's
5# Merges Trello board.
6#
7# Copyright (C) 2020 Bryce W. Harrington <bryce@bryceharrington.org>
8#
9# This program is free software: you can redistribute it and/or
10# modify it under the terms of the GNU Affero General Public
11# License as published by the Free Software Foundation, either
12# version 3 of the License, or (at your option) any later
13# version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23progname=$(basename "${0}")
24progdir=$(dirname "$(readlink -f "${0}")")
25scriptsdir="${progdir}/../scripts"
26
27usage() {
28 cat >&2 <<EOF
29Usage: ${progname} [options]
30
31Commands:
32 list Display your open cards
33 add <card-title) Add card with given title to current month
34 finish <card-id> Move card to Done column
35
36Options:
37 -h, --help This help listing
38EOF
39 exit $1
40}
41
42warn() {
43 echo "Warning: $1" 1>&2
44}
45
46die() {
47 echo "Error: $1" 1>&2
48 exit 1
49}
50
51card_id() {
52 local card="${1}"
53
54 if [[ "${card}" =~ "https://trello.com/"* ]]; then
55 # Looks like a card URL
56 echo "${card##*/}"
57 return 0
58 else
59 echo "${card}"
60 return 0
61 fi
62
63 warn "Could not parse card id from '${card}'"
64 return 1
65}
66
67#####################
68### Parse Options ###
69#####################
70
71parse_args() {
72 # Transform long options to short ones
73 for arg in "$@"; do
74 shift
75 case "${arg}" in
76 "--help") set -- "$@" "-h" ;;
77 "--*") usage 1 ;;
78 *) set -- "$@" "${arg}"
79 esac
80 done
81
82 # Parse short options
83 OPTIND=1
84 while getopts "h\?" opt $*; do
85 case "${opt}" in
86 h | \?) usage 0; ;;
87 * ) die "Unknown option '${opt}'" ;;
88 esac
89 done
90 shift $(expr ${OPTIND} - 1)
91}
92
93############
94### Main ###
95############
96
97function main() {
98 # Parse options
99 parse_args "$@" \
100 || die "Could not process options"
101 command="${1}" && shift
102 [ -n "${command}" ] || usage 1
103
104 if [ -e ~/.config/uscards.conf ]; then
105 source ~/.config/uscards.conf
106 elif [ -z "${TRELLO_USERNAME}" ]; then
107 die "TRELLO_USERNAME env var needs to be set to your trello account name"
108 fi
109
110 my_boards=$(so-trello member-boards)
111 merges_board_id=$(echo "${my_boards}" | grep -i "Merges Schedule" | cut -d' ' -f1)
112 merges_board_name=$(echo "${my_boards}" | grep -i "Merges Schedule" | cut -d' ' -f2-)
113 merges_lists=$(so-trello board-lists --board "${merges_board_id}")
114
115 backlog_lists=$(echo "${merges_lists}" | grep -i backlog)
116 active_lists=$(echo "${merges_lists}" | grep -iv backlog)
117
118 selected_lists="${active_lists}"
119
120 case "${command}" in
121 list)
122 echo "${merges_board_name}:"
123 for list in $(echo "${selected_lists}" | cut -d' ' -f1); do
124 list_name=$(so-trello list --list "${list}" | cut -d' ' -f2-)
125 cards=$(so-trello list-cards --list "${list}" --is-member "${TRELLO_USERNAME}")
126 if [ -n "${cards}" ]; then
127 echo "* ${list_name}:"
128 for card in "${cards}"; do
129 card_id=$(echo "${card}" | cut -d' ' -f1)
130 card_title=$(echo "${card}" | cut -d' ' -f2-)
131 echo "${card_title}" | sed -e 's/^/ - /'
132 # TODO: Get some card properties we want to display
133 # echo so-trello card --board "${merges_board_id}" --card "${card_id}" --raw json
134 done
135 fi
136 done
137 ;;
138
139 add)
140 local name="${@}"
141 [ -n "${name}" ] || usage 1
142
143 # By default, use the list for the current month
144 local list=$(date +%y.%m)
145 # TODO: Verify the list exists
146
147 # Check if stdin comes from terminal or a pipe
148 if [ -t 0 ]; then
149 # Terminal. Just add card with title.
150 so-trello list-addcard \
151 --board "${merges_board_id}" \
152 --list "${list}" \
153 --card-name "${name}"
154 echo "${name}:"
155 echo "${description}"
156 else
157 # Pipe. Use stdin as the description.
158 local description=$(cat <&0)
159 so-trello list-addcard \
160 --board "${merges_board_id}" \
161 --list "${list}" \
162 --card-name "${name}" \
163 --card-desc "${description}"
164 echo "${name}:"
165 echo "${description}"
166 fi
167 ;;
168
169 finish)
170 card=$(card_id "${1}")
171 [ -n "${card}" ] || usage 1
172
173 # TODO: Need to set 'DONE' on card
174 #so-trello card-update --board "${merges_board_id}" \
175 # --update-board "${merges_board_id}" --update-list "${list_done}" \
176 # --card "${card}"
177 ;;
178
179 help)
180 usage 0
181 ;;
182 *)
183 usage 1
184 ;;
185 esac
186
187 # Subcommands to add:
188 # * start <card> # Moves from TODO column to the Doing column
189 # * submit <card> # Moves to the Review column
190}
191
192if [[ "${0}" == "${BASH_SOURCE}" ]]; then
193 main "$@"
194fi
diff --git a/uscards/scripts/usmigration b/uscards/scripts/usmigration
0new file mode 100755195new file mode 100755
index 0000000..feadefd
--- /dev/null
+++ b/uscards/scripts/usmigration
@@ -0,0 +1,161 @@
1#!/bin/bash
2# -*- coding: utf-8; mode: sh -*-
3#
4# Proposed migration trello board CLI
5#
6# Copyright (C) 2020 Bryce W. Harrington <bryce@bryceharrington.org>
7#
8# This program is free software: you can redistribute it and/or
9# modify it under the terms of the GNU Affero General Public
10# License as published by the Free Software Foundation, either
11# version 3 of the License, or (at your option) any later
12# version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU Affero General Public License for more details.
18#
19# You should have received a copy of the GNU Affero General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22progname=$(basename "${0}")
23progdir=$(dirname "$(readlink -f "${0}")")
24scriptsdir="${progdir}/../scripts"
25
26usage() {
27 cat >&2 <<EOF
28Usage: ${progname} [options]
29
30Commands:
31 list Display migration issues that need attention
32 finish <card-title> Move card to Done column
33
34Options:
35 -h, --help This help listing
36EOF
37 exit $1
38}
39
40warn() {
41 echo "Warning: $1" 1>&2
42}
43
44die() {
45 echo "Error: $1" 1>&2
46 exit 1
47}
48
49card_id() {
50 local card="${1}"
51
52 if [[ "${card}" =~ "https://trello.com/"* ]]; then
53 # Looks like a card URL
54 echo "${card##*/}"
55 return 0
56 else
57 echo "${card}"
58 return 0
59 fi
60
61 warn "Could not parse card id from '${card}'"
62 return 1
63}
64
65
66#####################
67### Parse Options ###
68#####################
69
70parse_args() {
71 # Transform long options to short ones
72 for arg in "$@"; do
73 shift
74 case "${arg}" in
75 "--help") set -- "$@" "-h" ;;
76 "--*") usage 1 ;;
77 *) set -- "$@" "${arg}"
78 esac
79 done
80
81 # Parse short options
82 OPTIND=1
83 while getopts "h\?" opt $*; do
84 case "${opt}" in
85 h | \?) usage 0; ;;
86 * ) die "Unknown option '${opt}'" ;;
87 esac
88 done
89 shift $(expr ${OPTIND} - 1)
90}
91
92
93
94############
95### Main ###
96############
97
98function main() {
99 # Parse options
100 parse_args "$@" \
101 || die "Could not process options"
102
103 command="${1}" && shift
104 [ -n "${command}" ] || usage 1
105
106 if [ -e ~/.config/uscards.conf ]; then
107 source ~/.config/uscards.conf
108 elif [ -z "${TRELLO_USERNAME}" ]; then
109 die "TRELLO_USERNAME env var needs to be set to your trello account name"
110 fi
111
112 my_boards=$(so-trello member-boards)
113 migration_board_id=$(echo "${my_boards}" | grep -i "Proposed Migration" | cut -d' ' -f1)
114 migration_lists=$(so-trello board-lists --board "${migration_board_id}")
115
116 list_languishing=$(echo "${migration_lists}" | grep "Languishing" | cut -d' ' -f1)
117 list_todo=$(echo "${migration_lists}" | egrep "To *Do" | cut -d' ' -f1)
118 list_doing=$(echo "${migration_lists}" | egrep "Doing" | cut -d' ' -f1)
119 list_blocked=$(echo "${migration_lists}" | egrep "Blocked" | cut -d' ' -f1)
120 list_done=$(echo "${migration_lists}" | egrep "Done" | cut -d' ' -f1)
121 list_pending=$(echo "${migration_lists}" | egrep "Pending" | cut -d' ' -f1)
122
123 # TODO: Verify the above actually exist
124
125 case "${command}" in
126 list)
127 echo "Languishing:"
128 so-trello list-cards --list "${list_languishing}" --is-unassigned | cut -d' ' -f2- | sed -e 's/^/* /'
129 echo
130
131 echo "TODO:"
132 so-trello list-cards --list "${list_todo}" --is-unassigned | cut -d' ' -f2- | sed -e 's/^/* /'
133 echo
134
135 echo "Doing:"
136 so-trello list-cards --list "${list_doing}" --is-unassigned | cut -d' ' -f2- | sed -e 's/^/* /'
137 echo
138 ;;
139
140 finish)
141 card=$(card_id "${1}")
142 [ -n "${card}" ] || usage 1
143
144 so-trello card-update --board "${migration_board_id}" \
145 --update-board "${migration_board_id}" --update-list "${list_done}" \
146 --card "${card}"
147 ;;
148
149 help)
150 usage 0
151 ;;
152 *)
153 usage 1
154 ;;
155
156 esac
157}
158
159if [[ "${0}" == "${BASH_SOURCE}" ]]; then
160 main "$@"
161fi

Subscribers

People subscribed via source and target branches

to all changes: